I have a DynamicArray class shown below. (I have only included relevant methods. The rest can be viewed from https://www.geeksforgeeks.org/implementation-of-dynamic-array-in-python/)
import ctypes
class DynamicArray:
'''
Dynamic Array class
'''
def __init__(self):
self.n = 0 # count number of elements
self.capacity = 1 # default capacity
self.A = self.make_array(self.capacity)
def __len__(self):
"""
Return number of elements in the array
"""
return self.n
def __getitem__(self,k):
"""
Return element at k index
"""
#Check if K index is out of bounds#
if not 0 <= k < self.n:
return IndexError('{} is out of bounds'.format(k))
return self.A[k] #Retrieve from the array at index k#
Then I have another unit test file down below
from DynamicArray import DynamicArray
import unittest
class MyTestCase(unittest.TestCase):
def setUp(self) -> None:
self.a = DynamicArray() # empty array
self.b = DynamicArray()
self.b.append(0)
self.c = DynamicArray()
self.c.append(0)
self.c.append(1)
def test_getitem(self):
self.assertEqual(self.a.__getitem__(0),IndexError('0 is out of bounds'))
When I run the test I expect self.a.__getitem__(0) to throw IndexError('0 is out of bounds') and I can't see why the assertion fails? The only difference is that self.a.__getitem__(0) will yield IndexError('{} is out of bounds'.format(0)), which seems to me the same as IndexError('0 is out of bounds')
I tried running below code to see if the string by themselves were any different
if '{} is out of bounds'.format(0) == '0 is out of bounds':
print('str equal')
if '{} is out of bounds'.format(0).__len__() == '0 is out of bounds'.__len__():
print('len equal')
if IndexError('{} is out of bounds'.format(0)) == IndexError('0 is out of bounds'):
print('IndexError equal')
and confirmed that only the third if statement did not print
below is the photo of the console
Thanks in advance. Constructive criticisms and feedbacks are welcome.
Exceptions can't be conpared with assertEqual.
with self.assertRaises(IndexError, msg='0 is out of bounds'):
self.a[0]
And Exceptions must be raiseed to be captured.
You're returning IndexError
raise IndexError('{} is out of bounds'.format(k))
https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertRaises
Related
I have the following dataclass Gear that I want to limit the maximum value for gear_level from 0 to 5. But as you can see when I increment gear_level, it goes higher than 5, which is not what I want. I tried method as well as postinit. How do I fix this problem?
from dataclasses import dataclass
#dataclass
class Gear:
gear_level: int = 0
direction: str = None
# more codes ...
def __postinit__(self):
if self.gear_level <= 0:
self.gear_level = 0
elif 5 > self.gear_level > 0:
self.gear_level = self.gear_level
else:
self.gear_level = 5
def set_gear_level(self, level):
if level <= 0:
self.gear_level = 0
elif 5 > level > 0:
self.gear_level = level
else:
self.gear_level = 5
g = Gear()
g.set_gear_level(6)
print(g)
g.gear_level += 1
print(g)
g.set_gear_level(-1)
print(g)
g.gear_level -= 1
print(g)
Ideally, I prefer to use the g.gear_level += 1 notation, because I want to increment gear_level. It should not jump from gear level 1 to 5. Also, when it decrement, it should stop at 0. It should take both an assignment of 0 and be allowed to decrement to 0. Can this be done?
Gear(gear_level=5, direction=None)
Gear(gear_level=6, direction=None)
Gear(gear_level=0, direction=None)
Gear(gear_level=-1, direction=None)
In this case I would simply use a property:
#dataclass
class Gear:
gear_level: int
# Rest of the class excluded for simplicity
#property
def gear_level(self) -> int:
return self._gear_level
#gear_level.setter
def gear_level(self, value: int) -> None:
self._gear_level = min(max(value, 0), 5)
This way you don't need to write a __post_init__ or have to remember to call specific methods: assignment to gear_level will be kept 0 <= gear_level <= 5, even with +=.
The suggested link in the comments provides an elegant solution for tackling this issue, e.g. using a custom descriptor class which should work with minimal changes needed on your end.
For example, here's how I'd define a BoundsValidator descriptor class to check that a class attribute is within an expected lower and upper bounds (note that either bounds are optional in this case):
from typing import Optional
try:
from typing import get_args
except ImportError: # Python 3.7
from typing_extensions import get_args
class BoundsValidator:
"""Descriptor to validate an attribute x remains within a specified bounds.
That is, checks the constraint `low <= x <= high` is satisfied. Note that
both low and high are optional. If none are provided, no bounds will be
applied.
"""
__slots__ = ('name',
'type',
'validator')
def __init__(self, min_val: Optional[int] = None,
max_val: Optional[int] = float('inf')):
if max_val is None: # only minimum
def validator(name, val):
if val < min_val:
raise ValueError(f"values for {name!r} have to be > {min_val}; got {val!r}")
elif min_val is None: # only maximum
def validator(name, val):
if val > max_val:
raise ValueError(f"values for {name!r} have to be < {max_val}; got {val!r}")
else: # both upper and lower bounds are given
def validator(name, val):
if not min_val <= val <= max_val:
raise ValueError(f"values for {name!r} have to be within the range "
f"[{min_val}, {max_val}]; got {val!r}")
self.validator = validator
def __set_name__(self, owner, name):
# save the attribute name on an initial run
self.name = name
# set the valid types based on the annotation for the attribute
# for example, `int` or `Union[int, float]`
tp = owner.__annotations__[name]
self.type = get_args(tp) or tp
def __get__(self, instance, owner):
if not instance:
return self
return instance.__dict__[self.name]
def __delete__(self, instance):
del instance.__dict__[self.name]
def __set__(self, instance, value):
# can be removed if you don't need the type validation
if not isinstance(value, self.type):
raise TypeError(f"{self.name!r} values must be of type {self.type!r}")
# validate that the value is within expected bounds
self.validator(self.name, value)
# finally, set the value on the instance
instance.__dict__[self.name] = value
Finally, here's the sample code I came up with to test that it's working as we'd expect:
from dataclasses import dataclass
from typing import Union
#dataclass
class Person:
age: int = BoundsValidator(1) # let's assume a person must at least be 1 years
num: Union[int, float] = BoundsValidator(-1, 1)
gear_level: int = BoundsValidator(0, 5)
def main():
p = Person(10, 0.7, 5)
print(p)
# should raise a ValueError now
try:
p.gear_level += 1
except ValueError as e:
print(e)
# and likewise here, for the lower bound
try:
p.gear_level -= 7
except ValueError as e:
print(e)
# all these should now raise an error
try:
_ = Person(0, 0, 2)
except ValueError as e:
print(e)
try:
_ = Person(120, -3.1, 2)
except ValueError as e:
print(e)
if __name__ == '__main__':
main()
This provides the output below when we run the code:
Person(age=10, num=0.7, gear_level=5)
values for 'gear_level' have to be within the range [0, 5]; got 6
values for 'gear_level' have to be within the range [0, 5]; got -2
values for 'age' have to be within the range [1, inf]; got 0
values for 'num' have to be within the range [-1, 1]; got -3.1
There is also an excellent library called param that enables you to achieve this rather easily. In your case it would look something like this:
import param
#dataclass
class Gear:
gear_level: int = param.Integer(1, bounds=(0,5))
direction: str = None
g = Gear()
print(g)
g.gear_level = 42 # This throws an exception
It has many other neat features for designing robust interfaces.
Again, I am new to python. I need a FIFO with a limited depth.
F.e the depth is 5000, so after 5000 and more added items the first one's should be deleted to keep its depth is 5000. Some times I need to read 'the first' one and sometimes read the 'last one'. If I read the first one then it should be removed.
# class
class DictionaryDeque:
from collections import OrderedDict
def __init__(self, dequeDict=10):
self._stack = OrderedDict()
self._range = dictRange
self.setRange(dictRange)
self._len = 0
def len(self):
self._len = len(self._stack)
return self._len
def getRange(self):
return self._range
def setRange(self, range):
self._range = range
# change the dict range if the dict has more items
self.do_pop()
def add(self, key, value):
self._stack[key] = value
self.len()
self.do_pop()
def stack(self):
if self._len > 0:
self.do_pop()
return self._stack
else:
return ""
def last(self):
self.do_pop()
if self._len > 0:
return list(self._stack)[-1]
else:
return list(self._stack)[0]
def first(self):
self.do_pop()
return list(self._stack)[0]
def do_pop(self):
while self.len() > self._range:
self._stack.popitem(last=False)
self.len()
# end of class
dequeDict = DictionaryDeque(30)
for i in range (0, 40):
now = str(datetime.datetime.now())
dequeDict.add(now, i)
dequeDict.setRange(10)
print(dequeDict.len())
print(dequeDict.last())
print(dequeDict.first())
print(dequeDict.stack())
I have to implement the 'first read and remove' and some more functions, but before I start with that, I would love to know if this the/a way to go, or should there be something better?
Is there a way to avoid the list part in
list(self._stack)[0]
?
BTW, what is a good name for this class? < class name changed
Thank you
The title pretty much explains the problem. I don't know if there's a practical solution to this or if I'm being too picky over the behavior of my code.
This article was hinting in the right direction, but I never got any code to work.
https://medium.com/#adamshort/python-gems-5-silent-function-chaining-a6501b3ef07e
Here's an example of the functionality that I want:
class Calc:
def __init__(self, n=0):
self.n = n
def add(self, n):
self.n += n
return self
def might_return_false(self):
return False
def print(self):
print(self.n)
return self
w = Calc()
# The rest of the chain after might_return_false should be ignored
addedTwice = w.add(5).might_return_false().add(5).print()
w.print() # Should print 5
print(addedTwice) # Should print False
I think the article meant something more or less like below (but I prefer the other answer using exception, as it's more readable and better testable).
Create a helper class:
class Empty:
def __call__(self, *args, **kwargs):
return self
def __getattr__(self, *args, **kwargs):
return self
def print(self, *args, **kwargs):
return False
and
def might_return_false(self):
return Empty()
Exceptions are a great way to interrupt a chained operation:
class CalcError(Exception):
pass
class Calc:
def __init__(self, n: int = 0):
self.n = n
def add(self, n: int) -> 'Calc':
self.n += n
return self
def might_raise(self) -> 'Calc':
raise CalcError
def __str__(self) -> str:
return str(self.n)
w = Calc()
try:
w.add(5).might_raise().add(5)
addedTwice = True
except CalcError:
addedTwice = False
print(w) # prints 5
print(addedTwice) # prints False
You could also do chains like:
w = Calc()
num_added = 0
try:
w.add(5)
num_added += 1
w.add(5)
num_added += 1
w.might_raise()
w.add(5)
num_added += 1
w.add(5)
num_added += 1
except CalcError:
print(f"Stopped after {num_added} additions")
If you attempt to do this with return instead of raise, you need to check the status at each step of the chain so that you can switch off to some other branch of code (probably via an if block). Raising an exception has the very useful property of immediately interrupting execution, no matter where you are, and taking you straight to the nearest matching except.
I am trying to generate the following sequence:
011212201220200112 ... constructed as follows: first is 0,
then repeated the following action:
already written part is attributed to the right with replacement
0 to 1, 1 to 2, 2 to 0.
E.g.
0 -> 01 -> 0112 -> 01121220 -> ...
I am trying to find the 3 billion-th element of this sequence.
I realized that the sequence grows exponentially and hence derived that:
log(base2) (3 billion) ~ 32
So I just need to generate this sequence 32 times.
Here is what I tried in python:
import os
import sys
s=['0']
num_dict = {'0':'1' , '1':'2' , '2':'0'}
def mapper(b):
return num_dict[b]
def gen(s):
while True:
yield s
s.extend( map(mapper,s) )
a = gen(s)
for i in xrange(32):
a.next()
print a.next()[3000000000 - 1]
The problem is my RAM gets filled up before hitting the 3 billion mark.
Is there a better way to do this problem ?
EDIT: This program could crash your machine.Please try for xrange(25) for testing purposes
There are enough hints in the comments that you should be able to find the one-line solution. I think that it's more interesting to try to derive it with a more general tool, namely, implicit data structures. Here's a class for singleton lists.
class Singleton:
def __init__(self, x):
self.x = x
def __getitem__(self, i):
if not isinstance(i, int): raise TypeError(i)
elif not (0 <= i < len(self)): raise IndexError(i)
else: return self.x
def __len__(self): return 1
We can use this class like so.
>>> lst = Singleton(42)
>>> lst[0]
42
>>> len(lst)
1
Now we define a concatenation class and a mapper class, where the latter takes a function and implicitly applies it to each list element.
class Concatenation:
def __init__(self, lst1, lst2):
self.lst1 = lst1
self.lst2 = lst2
self.cachedlen = len(lst1) + len(lst2)
def __getitem__(self, i):
if not isinstance(i, int): raise TypeError(i)
elif not (0 <= i < len(self)): raise IndexError(i)
elif i < len(self.lst1): return self.lst1[i]
else: return self.lst2[i - len(self.lst1)]
def __len__(self): return self.cachedlen
class Mapper:
def __init__(self, f, lst):
self.f = f
self.lst = lst
def __getitem__(self, i): return self.f(self.lst[i])
def __len__(self): return len(self.lst)
Now let's rewrite your code to use these classes.
a = Singleton(0)
for i in range(32):
a = Concatenation(a, Mapper({0: 1, 1: 2, 2: 0}.get, a))
print(a[3000000000 - 1])
As an exercise: why do we need cachedlen?
I'm creating a vector class that has one parameter being the length of a vector. The length is automatically 0 if none is entered by user. If a vector is given a length, however, each number will be set to 0. For example: v(5) would be [0,0,0,0,0] and v() would be [].
This is the code i have thus far, but it's not quite working. Any advice?
class V:
def __init__(self, length = 0):
self.vector = [0]*length
def __str__(self):
print(self.vector)
def __len__(self):
return len(self.vector)
Then i plug in a = V() b = V(5) and when i print(a) and print(b) i get an TypeError. Any advice?
I'd probably cheat and go for sub-classing list:
class V(list):
def __init__(self, length=0):
super(V, self).__init__([0] * length)
This way you get the length, repr and other goodies for free.
class V:
def __init__(self, length = 0):
self.data = [0]*length
def __str__(self):
return '[{}]'.format(', '.join(str(d) for d in self.data))
def __len__(self):
return len(self.data)