Python magic method confusion - python

I've run into some confusing behaviour of the magic comparison methods.
Suppose we have the following class:
class MutNum(object):
def __init__ (self, val):
self.val = val
def setVal(self, newval):
self.val = newval
def __str__(self):
return str(self.val)
def __repr__(self):
return str(self.val)
# methods for comparison with a regular int or float:
def __eq__(self, other):
return self.val == other
def __gt__(self, other):
return self.val > other
def __lt__(self, other):
return self.val < other
def __ge__(self, other):
return self.__gt__(other) or self.__eq__(other)
def __le__(self, other):
return self.__lt__(other) or self.__eq__(other)
The class does what it is supposed to do, comparing a MutNum object to a regular int or float is no problem. However, and this is what I don't understand, it even compares fine when the magic methods are given two MutNum objects.
a = MutNum(42)
b = MutNum(3)
print(a > b) # True
print(a >= b) # True
print(a < b) # False
print(a <= b) # False
print(a == b) # False
Why does this work? Thanks.

It evaluates as follows (using a repr-like notation instead of referring to variables):
MutNum(42) > MutNum(3)
=> MutNum(42).__gt__(MutNum(3))
=> MutNum(42).val > MutNum(3)
=> 42 > MutNum(3)
And from there, it's just the int-MutNum comparision you already know works.

If you throw in some print's and/or sys.stderr.write's, I think you'll see what's happening. EG:
def __gt__(self, other):
sys.stderr.write('__gt__\n')
sys.stderr.write('{}\n'.format(type(other)))
sys.stderr.write('{} {}\n'.format(self.val, other))
result = self.val > other
sys.stderr.write('result {}\n'.format(result))
return result
def __lt__(self, other):
sys.stderr.write('__lt__\n')
sys.stderr.write('{}\n'.format(type(other)))
sys.stderr.write('{} {}\n'.format(self.val, other))
result = self.val < other
sys.stderr.write('result {}\n'.format(result))
return result
When you try to compare self.val (an int) to other (a MutNum), python realizes it has nothing for comparing an int to a MutNum, and reverses the order of the comparison, and compares a MutNum to an int - which is something you've defined. That is, a single > comparison is doing the > as you'd expect, but it's also doing a <.

Related

How to override or perform min/max in python on your own class? [duplicate]

This question already has answers here:
Is there a way to return a custom value for min and max in Python?
(3 answers)
Closed 2 years ago.
Not sure the best way to title this question, but how can I override or perform min(a, b) or max(a, b) on objects of a class i made? I can override the gt and lt like below but I would like to override the min or max so that I'll be able to use something like max(a, b, c ,d). The class will have multiple property as well, but I think 2 for this example is sufficient.
class MyClass:
def __init__(self, item1, item2):
self.item1 = item1
self.item2 = item2
def __gt__(self, other):
if isinstance(other, MyClass):
if self.item1 > other.item1:
return True
elif self.item1 <= other.item1:
return False
elif self.item2 > other.item2:
return True
elif self.item2 <= other.item2:
return False
def __lt__(self, other):
if isinstance(other, MyClass):
if self.item1 < other.item1:
return True
elif self.item1 >= other.item1:
return False
elif self.item2 < other.item2:
return True
elif self.item2 >= other.item2:
return False
Ex:
a = MyClass(2,3)
b = MyClass(3,3)
print(a > b)
# False
I tried overriding __cmp__ but that doesnt seem to work.
Would like to be able to do max(a, b) and return b object
Just override the comparison magic methods.
class A(object):
def __init__(self, value):
self.value = value
def __lt__(self, other):
return self.value < other.value
def __le__(self, other):
return self.value <= other.value
def __eq__(self, other):
return self.value == other.value
def __ne__(self, other):
return self.value != other.value
def __gt__(self, other):
return self.value > other.value
def __ge__(self, other):
return self.value >= other.value
def __str__(self):
return str(self.value)
a = A(10)
b = A(20)
min(a, b)

Python 3 Enums: Enum inheriting another Enum doesn't work?

I'm just trying to make an Enum in Python 3 by reference of the official Python docs https://docs.python.org/3.4/library/enum.html and specifically 8.13.13.2 and 8.13.13.4 examples.
My target is having an Enum which I can iterate, compare and also having three separate attributes. But I keep finding this error:
AttributeError: can't set attribute
It seems an error in __init__() constructor.
Code:
I tried firstly with one only class like this:
class Hand(Enum):
FIVE_OF_KIND = (6,'FIVE_OF_KIND',[5])
FOUR_OF_KIND = (5,'FOUR_OF_KIND',[4,1])
FULL_HOUSE = (4,'FULL_HOUSE',[3,2])
THREE_OF_KIND = (3,'THREE_OF_KIND',[3,1,1])
DOUBLE_PAIR = (2,'DOUBLE_PAIR',[2,2,1])
PAIR = (1,'PAIR',[2,1,1,1])
NOTHING = (0,'NOTHING',[1,1,1,1,1])
def __init__(self, val, name, struct):
self.val = val
self.name = name
self.struct = struct
def __ge__(self, other):
if self.__class__ is other.__class__:
return self.value >= other.value
return NotImplemented
def __gt__(self, other):
if self.__class__ is other.__class__:
return self.value > other.value
return NotImplemented
def __le__(self, other):
if self.__class__ is other.__class__:
return self.value <= other.value
return NotImplemented
def __lt__(self, other):
if self.__class__ is other.__class__:
return self.value < other.value
return NotImplemented
and secondly with two classes like this:
class OrderedEnum(Enum):
def __ge__(self, other):
if self.__class__ is other.__class__:
return self.value >= other.value
return NotImplemented
def __gt__(self, other):
if self.__class__ is other.__class__:
return self.value > other.value
return NotImplemented
def __le__(self, other):
if self.__class__ is other.__class__:
return self.value <= other.value
return NotImplemented
def __lt__(self, other):
if self.__class__ is other.__class__:
return self.value < other.value
return NotImplemented
class Hand(OrderedEnum):
FIVE_OF_KIND = (6,'FIVE_OF_KIND',[5])
FOUR_OF_KIND = (5,'FOUR_OF_KIND',[4,1])
FULL_HOUSE = (4,'FULL_HOUSE',[3,2])
THREE_OF_KIND = (3,'THREE_OF_KIND',[3,1,1])
DOUBLE_PAIR = (2,'DOUBLE_PAIR',[2,2,1])
PAIR = (1,'PAIR',[2,1,1,1])
NOTHING = (0,'NOTHING',[1,1,1,1,1])
def __init__(self, val, name, struct):
self.val = val
self.name = name
self.struct = struct
Enum objects already have a name attribute (for example, see 8.13.13.3), and apparently you are not allowed to set it – which makes sense when you think about how an enum should behave. You can achieve what you want like this:
from enum import Enum
class OrderedEnum(Enum):
# Same as your code.
class Hand(OrderedEnum):
FIVE_OF_KIND = (6, [5])
FOUR_OF_KIND = (5, [4,1])
FULL_HOUSE = (4, [3,2])
THREE_OF_KIND = (3, [3,1,1])
DOUBLE_PAIR = (2, [2,2,1])
PAIR = (1, [2,1,1,1])
NOTHING = (0, [1,1,1,1,1])
def __init__(self, val, struct):
# No need to set self.name. It's already handled.
self.val = val
self.struct = struct
for h in Hand:
print((h.name, h.val, h.struct))

Python: Strange behaviour of __eq__ and __contains__ on a custom class

I have the following custom classes (stripped down some), implementing an expression tree:
from abc import ABC, abstractmethod
class Operator:
def __init__(self, str_reps):
self.str_reps = str_reps
def __str__(self):
return self.str_reps[0]
def __eq__(self, other):
return self is other
def __ne__(self, other):
return self is not other
def __hash__(self):
return hash(str(self))
NOT = Operator(["¬", "~", "not", "!"])
AND = Operator(["∧", "&", "and"])
OR = Operator(["∨", "|", "or"])
class Node(ABC):
#abstractmethod
def __eq__(self, other):
pass
#abstractmethod
def __hash__(self):
pass
def __ne__(self, other):
return not self == other
#abstractmethod
def __str__(self):
pass
#abstractmethod
def __invert__(self):
pass
def bracket_if_necessary(self):
return str(self)
class Leaf(Node):
def __init__(self, v):
self.val = v
def __eq__(self, other):
if not isinstance(other, Leaf):
return False
return self.val == other.val
def __hash__(self):
return hash(self.val)
def __str__(self):
return str(self.val)
def __invert__(self):
return UnaryNode(self)
class UnaryNode(Node):
def __init__(self, child):
self.child = child
self.hash = hash(NOT) + hash(self.child)
def __eq__(self, other):
if not isinstance(other, UnaryNode):
return False
return self.child == other.child
def __hash__(self):
return self.hash
def __str__(self):
return str(NOT) + self.child.bracket_if_necessary()
def __invert__(self):
return self.child
class VariadicNode(Node):
def __init__(self, op, children):
self.op = op
self.children = children
self.hash = hash(self.op) + sum(hash(child) for child in self.children)
def __eq__(self, other):
if not isinstance(other, VariadicNode):
return False
return self.op is other.op and set(self.children) == set(other.children)
def __hash__(self):
return self.hash
def __str__(self):
return (" " + str(self.op) + " ").join(child.bracket_if_necessary() for child in self)
def __invert__(self):
return VariadicNode(AND if self.op is OR else OR, tuple(~c for c in self))
def bracket_if_necessary(self):
return "(" + str(self) + ")"
def __iter__(self):
return iter(self.children)
def __contains__(self, item):
return item in self.children
If I run this and try things like
Leaf("36") == Leaf("36)
~Leaf("36") == ~Leaf("36")
~~Leaf("36") == Leaf("36")
they all return True, as expected.
However, I'm running into bugs in the code that utilizes these nodes:
# Simplify procedure in DPLL Algorithm
def _simplify(cnf, l):
# print for debugging
for c in cnf:
print("b", l, ~l, type(l), "B", c, type(c), l in c)
return VariadicNode(AND, tuple(_filter(c, ~l) for c in cnf if l not in c))
# Removes the chosen unit literal (negated above) from clause c
def _filter(c, l):
# print for debugging
for x in c:
print("a", l, type(l), "A", x, type(x), x==l)
return VariadicNode(c.op, tuple(x for x in c if x != l))
Here cnf is given as a VariadicNode(AND) with all children being VariadicNode(OR). Children of VariadicNode are always given as a tuple.
These two prints result in lines like:
a ¬25 <class 'operators.UnaryNode'> A ¬25 <class 'operators.UnaryNode'> False
b ¬25 25 <class 'operators.UnaryNode'> B ¬25 ∨ ¬36 <class 'operators.VariadicNode'> False
which should not happen (¬25 == ¬25 in the first line and ¬25 in (¬25 ∨ ¬36) in the second should both return True). However there is also a line in the output:
b ¬25 25 <class 'operators.UnaryNode'> B ¬25 <class 'operators.VariadicNode'> True
so the check ¬25 in (¬25) actually does return True as it should.
Can anyone tell me what's going on?
If more info is needed, the rest of the code is available at [deleted] (hopefully it's publicly available, I'm pretty new to github so I don't know their policies). Note that it is still a WIP though.
The classes are located in operators.py and the rest of the (relevant) code is in SAT_solver.py, while test.py allows for easy running of the entire project, provided the networkx library is installed.
EDIT
I've now pushed a sample dimacs .txt file to the github repository that results in the described problem. Simply download hamilton.txt, SAT_solver.py and operators.py from the repository to the same folder, run SAT_solver.py (which has a main() method) and input hamilton or hamilton.txt to the command line when prompted for the problem file name (just leave the solution file name empty when prompted to prevent the program from writing any files). This should result in a lot of output, including problematic lines as described above.
The code you posted is not the code in your github repo. Your code has UnaryNode.__eq__ of
def __eq__(self, other):
if not isinstance(other, UnaryNode):
return False
return self.child == other.child
the repo code has
def __eq__(self, other):
if not isinstance(other, UnaryNode):
return False
return self.op is other.op and self.child == other.child
which also requires that the operators are identical. Instrumenting your code and breaking at a failure shows that you're generating two different NOT operators somewhere:
>>> str(x)
'¬24'
>>> str(l)
'¬24'
>>> x == l
False
>>> x.child == l.child
True
>>> str(x.op)
'¬'
>>> str(l.op)
'¬'
>>> x.op == l.op
False
>>> id(x.op)
2975964300
>>> id(l.op)
2976527276
Track down where that's happening and fix it however you like, whether by avoiding having more than one or by not caring whether there are more than one (my preference). I know in a deleted comment you wrote "The operators are single objects and I want them to be compared for equality based on their references", but (1) they're not single objects, and (2) if you didn't want such an unnecessary thing you wouldn't have woudn up in trouble..
If I had to guess, the other operators are being introduced when you call copy.deepcopy, but you're definitely not working with singletons.

Change representation of Python object

In Python, data types (like int, float) both represent a value, but also have some built-in attributes/functions/etc:
In [1]: a = 1.2
In [2]: a
Out[2]: 1.2
In [3]: a.is_integer()
Out[3]: False
Is it possible to reproduce this behavior within Python, e.g. define a class:
class Scalar:
def __init__(self, value)
self.value = value
# other code ....
s = Scalar(1.2)
where I could have s return 1.2 (instead of typing s.value), and do things like a = s -> a = 1.2? The closest I can get to this behavior is adding something like:
def __getitem__(self, key=None):
return self.value
and using a = s[()], but that doesn't look very good.
where I could have s return 1.2 (instead of typing s.value)
In the console? Then implement the __repr__ method.
a = s -> a = 1.2
To avoid having to use a = s.value, you can implement __call__ and call the object:
>>> class Scalar:
... def __init__(self, value):
... self.value = value
... def __repr__(self):
... return str(self.value)
... def __call__(self):
... return self.value
...
>>> s = Scalar(1.2)
>>> s
1.2
>>> a = s()
>>> a
1.2
Check the documentation about the data model on emulating numeric types.
For example:
class Scalar:
def __init__(self, value):
self.value = value
def __repr__(self):
return str(self.value)
def __call__(self):
return self.value
def __add__(self, other):
return Scalar(self.value + other.value)
def __lt__(self, other):
return self.value < other.value
def ___le__(self, other):
return self.value <= other.value
def __eq__(self, other):
return self.value == other.value
def __ne__(self, other):
return self.value != other.value
def __gt__(self, other):
return self.value > other.value
def __ge__(self, other):
return self.value >= other.value
Can be used like this:
>>> s1 = Scalar(1.2)
>>> s2 = Scalar(2.1)
>>> s1 + s2
3.3
>>> s1 < s2
True
>>> s1 > s2
False
>>> s1 != s2
True
>>> s1 <= s2
True
>>> s1 >= s2
False
There are also the __int__ and __float__ magic methods, which you can implement and use like this (this is more semantically correct):
>>> a = int(s)
>>> a = float(s)
As far as I know, that's not possible for your a = s example. You would have to change the behavior of =, the assignment operator. The assignment operator doesn't really do anything to the object on the right, it just copies a reference to it (in the case of an object, at least).
In general, it is possible to change the behavior of built in operators for your custom classes using operator overloading, but Python doesn't provide this sort of option for assignment (=) because of how different it is from operators like addition (+) and even equality (==).

Overwrite __sub__ for a-b when 'b' is my class

I have a class foo that is essentially a float with some extra attributes attached. I can overwrite its __sub__ method so that I can do subtraction one direction, but I can't figure out how to do it the other way:
class foo():
def __init__(self, value, otherstuff):
self.value = value
self.otherstuff = otherstuff
def __sub__(self, other):
return self.value - other
a = 5
b = foo(12, 'blue')
print b-a # this works fine and returns 7
print a-b # I want this to return -7 but it obviously doesn't work
Is there a way to do this?
A general solution for add, sub, mul, div would be ideal, but sub and div are most pressing since they're not reversible.
You just need to override __rsub__, for right-hand side subtraction:
class foo():
def __init__(self, value, otherstuff):
self.value = value
self.otherstuff = otherstuff
def __sub__(self, other):
return self.value - other
def __rsub__(self, other):
return other - self.value
Output:
print(b - a)
7
print(a - b)
-7
There are similar methods like __radd__, __rmul__ for other operations.

Categories

Resources