Python function call in class - python

I would like to make a class that returns a pseudo random number generator. I want to save the result of the _rand function in _r but when I call the function _rand(int(time.time())) I get the error message "rand() missing 1 required positional argument: 'seed'". Could someone please explain me what I did wrong.
class PRNG:
import time
_m = 32768
_b = 9757
_c = 6925
def _rand(self, seed):
n = seed % self._m
while True:
n = (n * self._b + self._c) % self._m
yield n
_r = _rand(int(time.time()))
def rand(self) :
return self._r.__next__()
prng = PRNG()
print(prng.rand())

_rand() takes two arguments; self and seed. Specifically you need to provide what instance the method is being called on. Instead of what you have, try defining _r in a constructor
def __init__(self):
self._m = 32768
self._b = 9757
self._c = 6925
self._r = self._rand(int(time.time()))

Related

How do I hard-code variables in a dynamically created function in python?

I have the following python3 code
class Test:
pos = [0,0]
actions = []
def bar(self, target):
for i in target:
def _():
print(i,end="")
self.actions.append(_)
foo = Test()
foo.bar("abcd")
for i in foo.actions:
i()
Which is meant to output:
abcd
but instead it outputs:
dddd
I'm pretty sure the function is using the value of i when executing (the last value i had) and not i's value the function _ is declared, which is what I want.
The general solution to this is to store the value as a default parameter value, like this:
class Test:
pos = [0,0]
actions = []
def bar(self, target):
for i in target:
def _(i=i): # This is the only changed line
print(i,end="")
self.actions.append(_)
foo = Test()
foo.bar("abcd")
for i in foo.actions:
i()
>>> abcd
Let's output some things:
class Test:
pos = [0, 0]
actions = []
def bar(self, target):
for i in target:
print(f"i={i} id={id(i)}")
def _():
print(f"_i={i} _id={id(i)}")
print(i, end="")
self.actions.append(_)
Output:
i=a id=2590411675120
i=b id=2590411458416
i=c id=2590411377456
i=d id=2590411377200
_i=d _id=2590411377200
d_i=d _id=2590411377200
d_i=d _id=2590411377200
d_i=d _id=2590411377200
See, the i in def _ overrides every time for loop iterates and eventually last value is what you get.
How to solve this? Pass i as an argument:
from functools import partial
class Test:
pos = [0, 0]
actions = []
def bar(self, target):
for i in target:
print(f"i={i} id={id(i)}")
def _(i):
print(f"_i={i} _id={id(i)}")
print(i, end="")
self.actions.append(partial(_, i))
Output:
i=a id=2618064721392
i=b id=2618064504688
i=c id=2618064423728
i=d id=2618064423472
_i=a _id=2618064721392
a_i=b _id=2618064504688
b_i=c _id=2618064423728
Let's remove print statements now:
from functools import partial
class Test:
pos = [0, 0]
actions = []
def bar(self, target):
for i in target:
def _(i):
print(i, end="")
self.actions.append(partial(_, i))
foo = Test()
foo.bar("abcd")
for i in foo.actions:
i()
# Output: abcd
the reason why you are getting the last value of i is that the function print takes reference to i and not literal value of it and since it's in a loop you will get the last value i had. a workaround to this problem would be as Mandera said to set function default parameter and this actually works because the value of i is stored in the function's attribute __defaults__, which is responsible of storing default parameters values.
So the final code would be :
class Test:
pos = [0,0]
actions = []
def bar(self, target):
for i in target:
def foo(p=i):print(p,end="")
self.actions.append(foo)
if __name__=="__main__":
foo = Test()
foo.bar("abcd")
for i in foo.actions:
i()
Note
the function foo isn't overridden

Sharing resources between two classes

I have provided a working example where I have a dynamic array implemented as custom type in Python 3. I wish to use a single instance of this dynamic array as a common resource for implementing, say, N stacks. How do I do that?
I imagine I would like to give each stack an access to only a certain part of the DynamicArray by making demarcation points _start and _end. In order to have _start and _end for each stack, I would like to wrap them in a helper class _StackRecord. In case I am successful in providing a modifiable view of DynamicArray, I want _StackRecord to do all the heavy lifting of poping and pushing such that the stacks don't collide while the underlying DynamicArray expands/shrinks as per the need. I know I am asking for too much, but I might learn some useless skills while I fail to do this.
Any suggestions/criticism towards modularity, maintainability and good practices are wholeheartedly welcome.
import ctypes
class DynamicArray:
"""Expandable array class similar to Python list"""
def __init__(self, size=0):
self._n = size
self._capacity = size + 1
self._A = self._make_low_level_array(self._capacity)
def _make_low_level_array(self, capacity):
return (capacity*ctypes.py_object)()
# following two methods are needed for Python to implement __iter__
def __len__(self):
return self._n
def __getitem__(self, index_key):
if isinstance(index_key, slice):
start, stop, step = index_key.indices(len(self))
return [self._A[i] for i in range(start, stop, step)]
elif isinstance(index_key, int):
if 0 <= index_key < self._n :
return self._A[index_key]
else:
raise IndexError("index out of bounds")
elif isinstance(index_key, tuple):
raise NotImplementedError('Tuple as index')
else:
raise TypeError('Invalid argument type: {}'.format(type(key)))
def __setitem__(self, index_k, value):
if 0 <= index_k < self._n :
self._A[index_k] = value
else:
raise IndexError("index out of bounds")
###################################################################
class FixedMultiStack:
class _StackRecord(DynamicArray):
def __init__(self, array: DynamicArray, stack_number=0, size_of_each=10):
self._stack = stack_number
self._start = stack_number*size_of_each
self._end = self._start + size_of_each
# try commenting the following lines
self._n = size_of_each
self._A = DynamicArray(self._n)
# If I have to use self._A then I would like it to point
# to array[self._start:self._end]
for i in range(self._start, self._end):
array[i] = i
for i in range(self._n):
self._A[i] = array[self._start+ i]
def __init__(self, numStack=1, sizeEach=10):
self._stacks = []
self._items = DynamicArray(numStack*sizeEach)
for i in range(numStack):
self._stacks.append(self._StackRecord(self._items, i, sizeEach))
def __getitem__(self, stack_number):
return self._stacks[stack_number]
if __name__ == "__main__":
fms = FixedMultiStack(3,10)
print(list(fms[0]))
print(list(fms[1]))
print(list(fms[2]))
print(list(fms._items))
Issues
I am doing the wasteful act of making a local copy called self._A. How do I avoid that? Why can't I just work on the global dynamic array passed to my local record keeper _StackRecord?
What do I expect?
fms = FixedMultiStack(3,10), A fixed multi stack packing 3 stacks of size 10 each such that
I would like, if self._A is necessary, the local self._A to refer to that part of DynamicArray which corresponds to the given stack number.
So that print(list(fms[n])) gives me the contents of nth stack
While print(list(fms._items)) should give me the combine state of all the stacks. Yikes! print(list(fms._items)) is ugly. How about print(list(fms))?
I should be able to write something like self._items[n].push(val), self._items[n].pop() to push and pop on n-th stack.
You can use memoryview for creating the different views over the entire array:
class FixedMultiStack:
def __init__(self, m, n):
self.data = bytearray(m*n)
view = memoryview(self.data)
self.stacks = [view[i*n:(i+1)*n] for i in range(m)]
def __getitem__(self, index):
return self.stacks[index]

Scrambling numbers

I am trying to program an algorithm that scrambles and "unscrambles" integer numbers.
I need two functions forward and backward
backward(number): return a "random" number between 0 and 9, the same input number always returns the same output
forward(number): return the input to backward that returns number
I managed to solve the problem like this:
from random import randint
class Scrambler:
def __init__(self):
self.mapping = [i for i in range(10)]
# scramble mapping
for i in range(1000):
r1 = randint(0, len(self.mapping) - 1)
r2 = randint(0, len(self.mapping) - 1)
temp = self.mapping[r1]
self.mapping[r1] = self.mapping[r2]
self.mapping[r2] = temp
def backward(self, num):
return self.mapping[num]
def forward(self, num):
return self.mapping.index(num)
if __name__ == '__main__':
s = Scrambler()
print(s.mapping)
for i in range(len(s.mapping)):
print(i, s.forward(i), s.backward(i), s.forward(s.backward(i)), s.backward(s.forward(i)))
Is there a way to do this without using the mapping list?
Can i calculate the return value of the functions forward and backward?
The "randomness" of the numbers does not need to be perfect.
I think your current solution is better than coming up with a function each time. It is a good solution.
Here is a generic solution for a generic key. You'd make your version using the Cipher.random_range method I've stuck on.
import random
class Cipher:
def __init__(self, key):
"""
key is a dict of unique values (i.e. bijection)
"""
if len(set(key.values())) != len(key):
raise ValueError('key values are not unique')
self._encoder = key.copy()
self._decoder = {v: k for k, v in key.items()}
#classmethod
def random_range(cls, max):
lst = list(range(max))
random.shuffle(lst)
return cls(dict(enumerate(lst)))
def encode(self, num):
return self._encoder[num]
def decode(self, num):
return self._decoder[num]

Class polynomial

I am learning classes in Python, I created a class called polynomial, and am trying to add two polynomials, but always get the following error message
soma.termos[i] = self[i] + other[i] TypeError: 'Polinomio' object
does not support indexing to fix
to fix I created an attribute that is the size of the vector and creates a vector of zeros of size n, but still, the error persists, what is wrong?
class Polinomio:
def __init__ (self, termos = [], n = 0):
self.termos = termos
self.n = [0] * n
def __len__ (self):
return len(self.termos)
def __setitem__ (self, i, x):
self.termos[i] = x
def __add__ (self, other):
soma = Polinomio(n = len(self.termos))
for i in range(len(self.termos)):
soma.termos[i] = self[i] + other[i]
def print (self):
print(self.termos)
p1 = Polinomio([1, 2, 3])
p2 = Polinomio([1, 2, 3])
p2.print()
p3 = Polinomio()
p3 = p1 + p2
You're not using your internal termos property when adding, instead you're trying to index your whole instance which, unsurprisingly, raises an error. Try changing your __add__ method to:
def __add__ (self, other):
soma = Polinomio(n = len(self.termos))
for i in range(len(self.termos)):
soma.termos[i] = self.termos[i] + other[i]
return soma
Or even better:
def __add__ (self, other):
soma = Polinomio(n = len(self.termos))
for i, v in enumerate(self.termos):
soma.termos[i] = v + other[i]
return soma
Also, do not initialize your termos list in your __init__ signature as it will always refer to the same list. Instead, declare it as None and build it as new whenever it's not passed, i.e.:
def __init__ (self, termos = None, n = 0):
self.termos = termos or []
self.n = [0] * n
You should add a method __getitem__:
def __getitem__(self, i):
return self.termos[i]
And also in your __add__ function, you instantiate a Polinomio by saying n = len(self.termos) but your using something called keyword arguments, so it will not actually instantiate a Polinomio with n as len(self.termos, you should instead say Polinomio([], len(self.termos) or implement keyword arguments if you want to use that syntax to intstantiate it.

Python Printing a Deque

I have an entire Deque Array class that looks like this:
from collections import deque
import ctypes
class dequeArray:
DEFAULT_CAPACITY = 10 #moderate capacity for all new queues
def __init__(self):
self.capacity = 5
capacity = self.capacity
self._data = self._make_array(self.capacity)
self._size = 0
self._front = 0
def __len__(self):
return self._size
def __getitem__(self, k): #Return element at index k
if not 0 <= k < self._size:
raise IndexError('invalid index')
return self._data[k]
def isEmpty(self):
if self._data == 0:
return False
else:
return True
def append(self, item): #add an element to the back of the queue
if self._size == self.capacity:
self._data.pop(0)
else:
avail = (self._front + self._size) % len(self._data)
self._data[avail] = item
self._size += 1
#def _resize(self, c):
#B = self._make_array(c)
#for k in range(self._size):
#B[k] = self._A[k]
#self._data = B
#self.capacity = capacity
def _make_array(self, c):
capacity = self.capacity
return (capacity * ctypes.py_object)()
def removeFirst(self):
if self._size == self.capacity:
self._data.pop(0)
else:
answer = self._data[self._front]
self._data[self._front] = None
self._front = (self._front + 1) % len(self._data)
self._size -= 1
print(answer)
def removeLast(self):
return self._data.popleft()
def __str__(self):
return str(self._data)
and when I try to print the deque in the main it prints out something like this,
<bound method dequeArray.__str__ of <__main__.dequeArray object at 0x1053aec88>>
when it should be printing the entire array. I think i need to use the str function and i tried adding
def __str__(self):
return str(self._data)
and that failed to give me the output. I also tried just
def __str__(self):
return str(d)
d being the deque array but I still am not having any success. How do I do i get it to print correctly?
you should call the str function of each element of the array that is not NULL, can be done with the following str function:
def __str__(self):
contents = ", ".join(map(str, self._data[:self._size]))
return "dequeArray[{}]".format(contents)
What I get when I try to q = dequeArray(); print(q) is <__main__.py_object_Array_5 object at 0x006188A0> which makes sense. If you want it list-like, use something like this (print uses __str__ method implicitly):
def __str__(self):
values = []
for i in range(5):
try:
values.append(self._data[i])
except ValueError: # since accessing ctypes array by index
# prior to assignment to this index raises
# the exception
values.append('NULL (never used)')
return repr(values)
Also, several things about the code:
from collections import deque
This import is never user and should be removed.
DEFAULT_CAPACITY = 10
is never used. Consider using it in the __init__:
def __init__(self, capacity=None):
self.capacity = capacity or self.DEFAULT_CAPACITY
This variable inside __init__ is never user and should be removed:
capacity = self.capacity
def _make_array(self, c):
capacity = self.capacity
return (capacity * ctypes.py_object)()
Though this is a valid code, you're doing it wrong unless you're absolutely required to do it in your assignment. Ctypes shouldn't be used like this, Python is a language with automated memory management. Just return [] would be fine. And yes, variable c is never used and should be removed from the signature.
if self._data == 0
In isEmpty always evaluates to False because you're comparing ctypes object with zero, and ctypes object is definitely not a zero.

Categories