Define condition to Class - python

I am trying to define condition to class, if my object Does not meet the conditions:
The conditions: all the organs is vectors, the shape of the vectors is the same.
When I try to casting the object the function will return None.
Thats what I tried so far:
class Matrix:
def __init__(self,m1):
dic_len = {}
self.m1 = tuple(m1)
checking = 0
for i in self.m1:
dic_len[len(i)] = 'check'
if type(i) != Vector:
self.m1 = None
checking = 1
if len(dic_len) != 1:
self.m1 = None
if len(dic_len) == 1 and checking == 0:
self.content = self.m1 = tuple(m1)
self.shape = (len(m1),len(m1[0]))
def __repr__(self):
if self.m1 != None:
return "(" + ", ".join(str(i) for i in self.m1) + ")"
else:
return None
But I get this error:
>>>v1 = Vector([1,2])
>>>v2 = Vector([4,5,6])
>>>m = Matrix([v1,v2])
>>>print(m)
TypeError: __str__ returned non-string (type NoneType)
i wish the function will return None.

return str(None)
instead of
return None

The CPython docs state for the __repr__ method state that
The return value must be a string object.
So returning None isn't going to work.
>>> class C:
... def __repr__(self):
... return None
...
>>> c = C()
>>> repr(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __repr__ returned non-string (type NoneType)
If you're going to share your code with others, it might be better to code __repr__ to produce its coventional output:
...this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment)...
And override __str__ to produce a representation that indicates the validity of the object instance (though note __str__ must also return a string).

Related

Is there any way I can change the way list() function works on my class?

I am writing a small class 'AP' ('Arithmetic Progression') for my school assignment. I have included an option for making the AP infinitely long.
class AP:
def __init__(self, first_term: float, co_diff: float, n: int = inf) -> None:
self.a1 = first_term
self.d = co_diff
self.term = 0
self.n = n
def __repr__(self) -> str:
return f'AP({self.a1}, {self.d}, {self.n})'
def __str__(self) -> str:
output_str = f"{', '.join([str(term) for term in list(self.terms(term=6))])}, ...."
if self.n != inf:
return output_str + f', {(self.a1 + ((self.n - 1) * self.d))}'
return output_str
def terms(self, term: int):
return AP(self.a1, self.d, n=term)
def __iter__(self):
return self
def __next__(self):
self.term += 1
if self.term < self.n:
return (self.a1 + ((self.term - 1) * self.d))
raise StopIteration
def __eq__(self, o: object) -> bool:
if isinstance(o, AP):
return str(self) == str(o)
But when someone wants to convert it into a list and if it is an infinite one, I want to raise an error like:
ValueError: The list() function was called for an object of undefined length.
Same goes for sum() function as well. Are there any solutions to this?
AFAIK, Python has no notion of explicit conversion operator implemented in the source class (C++ has). The conversion is the responsibility of the destination class and currently list has no provision for infinity.
A possible workaround would be to hide list using a local function:
def list(it=()):
import builtins
if isinstance(it, AP) and it.n == inf:
raise ValueError('The list() function was called for an object'
' of undefined length.')
return builtins.list(it)
The limit is that if you use sorted which also returns a list, your special function will not be used.
Another limit, if your class is declared in a specific module and used from other modules is that the special function has to be imported in the other modules with from APmod import list
A possible trick would be to use the __length_hint__ special method. It is used at least in CPython for all standard conversions of an iterable to a sequence type, and AFAIK should only be used there. You cannot return a value to tell list that the length would be infinite but you can raise the ValueError directly:
def __length_hint__(self):
if self.n is inf:
raise ValueError('This object has undefined length.')
return self.n - 1
Example after adding this method to your class:
>>> ap = AP(1., 1.)
>>> ap
AP(1.0, 1.0, inf)
>>> next(ap) # iteration is correct
1.0
>>> next(ap)
2.0
>>> next(ap)
3.0
>>> next(ap)
4.0
>>> list(ap) # list raises ValueError
Traceback (most recent call last):
File "<pyshell#75>", line 1, in <module>
list(ap)
File "<pyshell#68>", line 34, in __length_hint__
raise ValueError('This object has undefined length.')
ValueError: This object has undefined length.
>>> tuple(ap) # so does tuple
Traceback (most recent call last):
File "<pyshell#76>", line 1, in <module>
tuple(ap)
File "<pyshell#68>", line 34, in __length_hint__
raise ValueError('This object has undefined length.')
ValueError: This object has undefined length.
>>> sorted(ap) # or sorted
Traceback (most recent call last):
File "<pyshell#77>", line 1, in <module>
sorted(ap)
File "<pyshell#68>", line 34, in __length_hint__
raise ValueError('This object has undefined length.')
ValueError: This object has undefined length.
But as long as you have a finite length, all is fine:
>>> ap = AP(1., 1., 4)
>>> list(ap)
[1.0, 2.0, 3.0]
By the way, you could use the same implementation for the special __len__ method to allow your class to be used by the len builtin function.

How to add __len__ to an object without __len__ on its data type definition?

According to the documentation, this does not work because of this:
For custom classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary. That behaviour is the reason why the following code raises an exception:
>>> class C:
... pass
...
>>> c = C()
>>> c.__len__ = lambda: 5
>>> len(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'C' has no len()
https://docs.python.org/3/reference/datamodel.html#special-method-lookup
I had tried this on a function generator, which does not have __len__, but I knew beforehand its length, then, I tried monkey patch it with something like c.__len__ = lambda: 5, but it kept saying the generator object had no length.
This is the generator:
def get_sections(loaded_config_file):
for module_file, config_parser in loaded_config_file.items():
for section in config_parser.sections():
yield section, module_file, config_parser
I was passing the generator (which has no length) to this other function (yet, another generator), which requires the iterable length by calling len():
def sequence_timer(sequence, info_frequency=0):
i = 0
start = time.time()
if_counter = start
length = len(sequence)
for elem in sequence:
now = time.time()
if now - if_counter < info_frequency:
yield elem, None
else:
pi = ProgressInfo(now - start, float(i)/length)
if_counter += info_frequency
yield elem, pi
i += 1
https://github.com/arp2600/Etc/blob/60c5af803faecb2d14b5dd3041254ef00a5a79a9/etc.py
Then, when trying to add the __len__ attribute to get_sections, hence the error:
get_sections.__len__ = lambda: calculated_length
for stuff, progress in sequence_timer( get_sections ):
section, module_file, config_parser = stuff
TypeError: object of type 'function' has no len()
You can't add it to an existing object, so make your own wrapper class that has a class level definition you control:
class KnownLengthIterator:
def __init__(self, it, length):
self.it = it
self.length = int(length)
def __len__(self):
return self.length
def __iter__(self):
yield from self.it
Now you just change your invalid attempt to set a length of:
get_sections.__len__ = lambda: calculated_length
to a valid rewrapping that makes get_sections continue to be a valid generator (yield from will delegate all iteration behaviors to the wrapped generator), while exposing a length too:
get_sections = KnownLengthIterator(get_sections, calculated_length)
No other code needs to change.
You can do it in the following way
class A:
def __init__(self):
self.x = 1
a = A()
def b_fn(self):
return self.x
def __len__(self):
return 5
a.b = b_fn.__get__(a)
# A can be also obtained as type(a).
A.__len__ = __len__.__get__(A)
a.b()
len(a)
5

Python - __init__() missing 1 required positional argument:

I'm kinda new to python and I can't get past this error:
Traceback (most recent call last):
File "***", line 63, in <module>
bst = Node()
TypeError: __init__() missing 1 required positional argument: 'val'
Basically, the program is a BST which would allow you to insert, search and look for the minimum item by only going left.
Here's the code (sorry, it's hungarian)
class Node:
def __init__(self, val):
self.ertek = val
self.balgyerek = None
self.jobbgyerek = None
self.gyoker = None
def beszur(self, pri):
if self.gyoker:
return self.gyoker.beszur(pri)
else:
self.gyoker = Node(pri)
return True
if self.ertek == pri:
return False
elif self.ertek > pri:
if self.balgyerek:
return self.balgyerek.beszur(pri)
else:
self.balgyerek = Node(pri)
return True
else:
if self.jobbgyerek:
return self.jobbgyerek.beszur(pri)
else:
self.jobbgyerek = Node(pri)
return True
def keres(self, pri):
if self.gyoker:
return self.gyoker.keres(pri)
else:
return False
if(self.ertek == pri):
return True
elif self.ertek > pri:
if self.balgyerek:
return self.balgyerek.keres(pri)
else:
return False
else:
if self.jobbgyerek:
return self.jobbgyerek.keres(pri)
else:
return False
def minimumertek(self):
jelenlegi = self
while(jelenlegi.balgyerek is not None):
jelenlegi = jelenlegi.balgyerek
return self.ertek
bst = Node()
The __init__ method is run as soon as an object of a class is instantiated. Your __init__ method has two positional arguments: self which refers to the object instance and is passed automatically, and val which is assigned to self.ertek. However, you didn't pass the value of val. Hence, the error. Try passing the value of val at the class instantiation. e.g bst = Node('ertek value')

Python bytearray() index assignment

Bear with me here; I am a sysadmin not a developer. The following code works just fine for me. But when I break it into two files so that the class is in one file and the logic is in another I get an error that data[0] is a str and does not support assignment
Striped down working example
class partition:
def __init__(self):
self.data = bytearray(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
return
def boot_flag(self, value=None):
if value is not None:
self.data[0] = value
return
else:
return self.data[0:1][::-1]
part1 = partition()
print str(part1.data).encode('hex')
value = b"\xff"
part1.boot_flag(value)
print str(part1.data).encode('hex')
This is the full class as it stands right now.
class partition:
def __init__(self):
self.data = bytearray(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
def boot_flag(self, value=None):
if value is not None:
self.data[0] = value
return
else:
return self.data[0:1][::-1]
def start_chs(self, value=None):
if value is not None:
self.data[1:4] = value
return
else:
return self.data[1:4][::-1]
def type(self, value=None):
if value is not None:
self.data[4:5] = value
return
else:
return self.data[4:5][::-1]
def end_chs(self, value=None):
if value is not None:
self.data[5:8] = value
else:
return self.data[5:8][::-1]
def start_lba(self, value=None):
if value is not None:
self.data[8:12] = value
else:
return self.data[8:12][::-1]
def sectors(self, value=None):
if value is not None:
self.data[12:16] = value
else:
return self.data[12:16][::-1]
def load(self, data):
self.data = data
return
This is the test jig I am using to test the class.
import dospart
disk = open('/dev/sda', 'rb')
mbr = disk.read(512)
part1 = dospart.partition()
part1.load(mbr[446:462])
print str(part1.data).encode('hex')
part1.boot_flag(b"\xff")
print str(part1.data).encode('hex')
This is the error
Traceback (most recent call last):
File "test.py", line 13, in <module>
part1.boot_flag(b"\xff")
File "/Users/digitaladdictions/PycharmProjects/dospart/dospart.py", line 9, in boot_flag
self.data[0] = value
TypeError: 'str' object does not support item assignment
Note that I can read the values just fine. I only get an error when I try to write to self.data[]
[UPDATE]
Based on the accepted answer this is my new load function which works.
def load(self, data):
part = bytearray(data)
self.data = part
return
I think this is what is happening. When you invoke:
part1.load(mbr[446:462])
self.data is being assigned a string. And that point on, self.data is a string and not a byte array. So when you do
part1.boot_flag(b"\xff")
it rightly complains TypeError: 'str' object does not support item assignment
This is what I mean:
>>> data_one = "My name is shaktimaan"
>>> data_two = data_one[5:10]
>>> type(data_one)
<type 'str'>
In your first case, there is no invocation of load and hence self.data is still a byte array (after calling the constructor). So your boot_flag works as expected without complaining.
I think you need to fix the code in load to assign byte array to self.data
You can't change Python strings inplace, the're immutable. You can find a lot comments about that error "'str' object does not support item assignmen". I don't know how it can work if you combine it in one file.

Why does Python call __str__ instead of returning long value

I have a simple class that extends long to accept a string with value modifiers (ie '10m' would be 1024*1024*10)
I have the __str__ function that prints the original value passed in (ie if '10m' is passed in, return '10m')
Problem is that when I call something such as:
>>> printf("%d" % Size('10m'))
I get the following
SystemError: ../Objects/stringobject.c:4044: bad argument to internal function
Obviously if I print "%s" I get '10m'
So the question is, since I'm subclassing long, why does the class call __str__ when it should be getting the long value.
BTW, a bit more testing shows that the %x and %f will print the integer value which confuses me more. I also tried adding the __format__ but that appears to only be called on when "...".format() is called.
EDIT #1, Here's the code:
class Size(long):
'''Represents a size reflected bytes. Subclass of long.
Size passed in must be in the formats <int> or "0x<int>" or "0x<int><unit>" or "<int><unit>" or "<int><unit><int><unit>....".
"0x<int><unit>0x<int><unit>" or similar numbers are not supported as is "<int><unit><int>"
b = bytes
s = sectors (512-byte)
k = kilobytes
m = megabytes
g = gigabytes
t = terabytes
'''
units = { 'b':1, 's':512, 'k':1024, 'm':1024 ** 2, 'g':1024 ** 3, 't':1024 ** 4 }
def __new__(cls, value):
'''Creates a Size object with the specified value.
Value can be a number or a string (optionally prefixed with '0x' or
postfixed with a type character). If using hex, the final character
will be treated as part of the value if it is a hex digit, regardless
of whether it is a valid unit character.
Examples:
Size(50)
Size("0x100s") # 256 sectors
Size("64")
Size("512k")
Size("0x1b") # this is 1b bytes, not 1 byte
'''
self = _new_unit_number(value, cls.units, long, cls)
return self
def __init__(self, value):
self._orig_value = value
def __str__(self):
print "calling str"
return str(self._orig_value) # Convert to str in case the object was created w/an int
def __format__(self, format_spec):
print "calling format"
print format_spec
try:
value = format(str(self), format_spec)
except ValueError:
value = format(int(self), format_spec)
return value
def _new_unit_number(value, unit_list, num_type, cls):
'''Converts a string of numbers followed by a unit character to the
requested numeric type (int or long for example).
'''
base = 10
start = 0
digits = string.digits
try:
if value[0:2] == '0x':
start = 2
base = 16
digits = string.hexdigits
if value[-1] in digits:
return num_type.__new__(cls, value[start:], base)
else:
try:
# Use a regex to split the parts of the unit
regex_string = '(\d+[%s])' % (''.join(unit_list.keys()))
parts = [x for x in re.split(regex_string, value[start:]) if x]
if len(parts) == 1:
return num_type.__new__(cls, num_type(value[start:-1], base) * unit_list[value[-1]])
else:
# Total up each part
# There's probably a better way to do this.
# This converts each unit to its base type, stores it in total,
# only to be converted back to the base type.
total = 0
for part in parts:
total += num_type(part[start:-1], base) * unit_list[part[-1]]
# Finally return the requested unit
return num_type.__new__(cls, total)
except KeyError:
raise ValueError("Invalid %s unit identifier: %s"
% (cls.__name__, unit_list[value[-1]]))
# not a string or empty, see if we can still use the class's constructor
except (TypeError, IndexError):
return num_type.__new__(cls, value)
Not really an answer, but too long for a comment.
I find this question highly interesting. I tried to replicate the behaviour using this:
#! /usr/bin/python2.7
class Size (long):
def __new__ (cls, arg):
if arg and type (arg) == str:
if arg [-1] == 'm':
return super (Size, cls).__new__ (cls, long (arg [:-1] ) * 2 ** 20)
return super (Size, cls).__new__ (cls, arg)
def __init__ (self, arg):
self.s = arg
def __str__ (self):
return self.s
a = Size ('12m')
print (a)
print ('%s' % a)
#The following fails horribly
print ('%d' % a)
Behaviour as described by OP. But now comes the funny part: When I inherit from int and not from long, it works smoothly:
class Size (int):
def __new__ (cls, arg):
if arg and type (arg) == str:
if arg [-1] == 'm':
return super (Size, cls).__new__ (cls, int (arg [:-1] ) * 2 ** 20)
return super (Size, cls).__new__ (cls, arg)
def __init__ (self, arg):
self.s = arg
def __str__ (self):
return self.s
That is, it works fine in python2, but fails in python3. Strange, strange.
Please see Python issue tracker, Issue 18780: SystemError when formatting int subclass:
>>> class I(int):
... def __str__(self):
... return 'spam'
...
>>> '%d' % I(42)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
SystemError: Objects/unicodeobject.c:13305: bad argument to internal function
This works in 3.4.0alpha4, but not in 3.[0123].

Categories

Resources