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

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))

Related

How to return a default attribute for a class' instance?

I have a class with the following structure:
class Example:
def __init__(self, value, foo):
self.value = value
self.foo = foo
self.bar = self.modify_stuff(value, foo)
def modify_stuff(self, value, foo):
""" some code """
pass
I want to create an instance of the class, and then be able to refer to value directly, like this:
ex = Example(3, 'foo')
ans = 5 + ex
Instead of:
ans = 5 + ex.value
How can I do this?
Rewrite the add and radd:
class Example:
def __init__(self, value):
self.value = value
def __add__(self, other):
if isinstance(other, Example):
return self.value + other.value
return self.value + other
def __radd__(self, other):
if isinstance(other, Example):
return self.value + other.value
return self.value + other
print(Example(3) + 5)
print(Example(4) + Example(2))
# 8
# 6

Perform deep copy on assignment for instances of a specific class in Python3

I have a class in Python which is little more than the primitive values, like int or float, see below
class Entry:
def __init__(self, value, timestamp):
self.value = value
self.timestamp = timestamp
def __str__(self):
return"[v= {}, ts= {}]".format(self.value, self.timestamp)
def __hash__(self):
return hash(self.timestamp)
def __eq__(self, other):
return self.timestamp == other.timestamp
def __le__(self, other):
return self.timestamp <= other.timestamp
def __lt__(self, other):
return self.timestamp < other.timestamp
def __ge__(self, other):
return self.timestamp >= other.timestamp
def __gt__(self, other):
return self.timestamp > other.timestamp
def __copy__(self):
new_entry = Entry(deepcopy(self.value), self.timestamp)
print("hi")
return new_entry
e1 = Entry("some name", 10)
e2 = e1
e2.timestamp = 20
print(e1)
I want it to behave just like the primitive types as well. So when an assignment occurs, like above, the value is deep-copied, so I don't have to think about doing it manually everywhere I do assigment like this.
As you can see, I tried overriding the __copy__ method. Unfortunely that method isn't called here. Is there another method to override? I'm pretty sure this can be accomplished in C++. Can it be done in Python too?
You can't override the = assignment operator in Python, because it isn't a "copy" operator. Instead it binds an object to a value. You can, however, use the copy module, as described here: https://docs.python.org/3/library/copy.html.

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)

Can max be coerced to return only a float

I have a class that has two attributes (index and value). I want to be able to call max and return only the value(which is a float).
p = [Point(1, 23.33), Point(2, 12.3)]
max(p)
#<Point object at 0x1052d4eb8>
# I would like to see 23.33
# float(max(p)) returns the float but I want to be able to use the max function by it self
Is there a method in the data model that I can override to force max to return just the float? The entire class below.
Python 3.6
class Point:
""" a set of data indexed by time """
def __init__(self, index, value):
self._index = index
self._value = value
def __float__(self):
return float(self._value)
def __add__(self, other):
return float(self._value) + other
def __radd__(self, other):
return self.__add__(other)
def __lt__(self, other):
return self._value < float(other)
def ___le__(self, other):
return self._value <= float(other)
def __eq__(self, other):
return self._value == float(other)
def __ne__(self, other):
return self._value != float(other)
def __gt__(self, other):
return self._value > float(other)
def __ge__(self, other):
return self._value >= float(other)
According to the documentation, you can use any iterable: for instance you can iterate the values like this:
points = [Point(1, 23.33), Point(2, 12.3)]
m = max(p._value for p in points)
Or iterate using float:
m = max(float(p) for p in points)

Python magic method confusion

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 <.

Categories

Resources