Generally if we need to insert an object to a set, we should make it hash-able (by implementing the hash function) and comparable (and implementing a compare function). Set does not provide a mechanism to access its elements and thus cannot be mutated directly though can easily be circumvented.
A general pattern to mutate a set item would be as follows
i = next(iter(x))
update(i)
x.add(i)
This generally seem to work for almost all cases except one when unexpected holes are created.
class Foo(object):
def __init__(self, x):
self.x = x
self.count = 0
def __hash__(self):
return hash((self.x, ))
def __iadd__(self, n):
self.count += n
def __eq__(self, other):
return self.x == other.x
>>> x = {Foo(1)}
>>> i = next(iter(x))
>>> i+=1
>>> x.add(i)
>>> x
set([None, <__main__.Foo object at 0x0279D8B0>])
My guess is mutating a set element while updating may cause unexpected behavior but invoking next would just fetch the value (a copy I guess) that should not be an issue.
Any idea what the problem may be?
Per the docs,
[__iadd__] should attempt to do the operation in-place (modifying self) and
return the result (which could be, but does not have to be, self)
Therefore,
def __iadd__(self, n):
self.count += n
return self
Then,
class Foo(object):
def __init__(self, x):
self.x = x
self.count = 0
def __hash__(self):
return hash((self.x, ))
def __iadd__(self, n):
self.count += n
return self
def __eq__(self, other):
return self.x == other.x
x = {Foo(1)}
i = next(iter(x))
i+=1
x.add(i)
print(x)
yields
set([<__main__.Foo object at 0x7f19ae8b9f10>])
You probably want to return self in iadd method.
Related
I wrote a class that stores a list of objects which I have also defined.
I would like to be able to call obj_list.sort(), and have the results sorted in ascending order, but it isn't working out exactly how I want it.
If I get the obj data and call sort() three times this is the behavior with my current implementation:
class MyClass():
def __init__(self):
self.obj_list = self.set_obj_list()
def set_obj_list(self):
data = []
for x in range(20):
obj = MyObjClass(x)
data.append( obj )
data.sort()
return data
class MyObjClass():
def __init__(self, number):
self.number = number # number is an integer
def __lt__(self, other):
return cmp(self.number, other.number)
def __repr__(self):
return str(self.number)
a = MyClass()
print a.obj_list
a.obj_list.sort()
print a.obj_list
a.obj_list.sort()
print a.obj_list
a.obj_list.sort()
print a.obj_list
Thank you.
I want it sorted in ascending order, but for sort() to do nothing if already sorted.
__lt__ should return a true value if & only if self is less than other, but cmp(self, other) will return a true value (1 or -1) if self does not equal other. You need to change this:
def __lt__(self, other):
return cmp(self.number, other.number)
to this:
def __lt__(self, other):
return cmp(self.number, other.number) < 0
I want to write a program that accepts as input a number p and produces as output a type-constructor for a number that obeys integer arithmetic modulo p.
So far I have
def IntegersModP(p):
N = type('IntegersMod%d' % p, (), {})
def __init__(self, x): self.val = x % p
def __add__(a, b): return N(a.val + b.val)
... (more functions) ...
attrs = {'__init__': __init__, '__add__': __add__, ... }
for name, f in attrs.items():
setattr(N, name, f)
return N
This works fine, but I'd like to know what the Pythonic way to do this is, which I understand would use metaclasses.
Like this:
def IntegerModP(p): # class factory function
class IntegerModP(object):
def __init__(self, x):
self.val = x % p
def __add__(a, b):
return IntegerModP(a.val + b.val)
def __str__(self):
return str(self.val)
def __repr__(self):
return '{}({})'.format(self.__class__.__name__, self.val)
IntegerModP.__name__ = 'IntegerMod%s' % p # rename created class
return IntegerModP
IntegerMod4 = IntegerModP(4)
i = IntegerMod4(3)
j = IntegerMod4(2)
print i + j # 1
print repr(i + j) # IntegerMod4(1)
Metaclasses are for when your class needs to behave differently from a normal class or when you want to alter the behavior of the class statement. Neither of those apply here, so there's really no need to use a metaclass. In fact, you could just have one ModularInteger class with instances that record their value and modulus, but assuming you don't want to do that, it's still easy to do this with an ordinary class statement:
def integers_mod_p(p):
class IntegerModP(object):
def __init__(self, n):
self.n = n % IntegerModP.p
def typecheck(self, other):
try:
if self.p != other.p:
raise TypeError
except AttributeError:
raise TypeError
def __add__(self, other):
self.typecheck(other)
return IntegerModP(self.n + other.n)
def __sub__(self, other):
...
IntegerModP.p = p
IntegerModP.__name__ = 'IntegerMod{}'.format(p)
return IntegerModP
Good day!
I'm think about class in python to store a map tiles inside, such as
map = [[WorldTile() for _ in range(10)] for _ in range(10)]
i create class
class WorldTile:
def __init__(self, val):
self.resource = val
self.objects = dict()
def __repr__(self):
return self.resource
def __str__(self):
return '%s' % (str(self.resource))
def __cmp__(self, other):
return cmp(self.resource, other)
def __add__(self, other):
self.resource += other
return self.resource
def __sub__(self, other):
self.resource -= other
return self.resource
but something going wrong.
i'l try
x = WorldTile.WorldTile(7)
print type(x), id(x), x
print x > 2, x < 5, x > 0
#x += 5
print type(x), id(x), x
print x, str(x), type(x)
print x.objects
they work fine, but if i'l uncomment line x += 5 x becoming an <type 'int'>
totally, i'm want to have class, with i can work as integer ( x = x +-*\ y etc ), but also can access additional fields if necessary ( x.objects )
i think i need override assignemet method, but that not possible in python. Any other way for me?
You could override __iadd__ for +=.
However, your current __add__ is broken. You could fix it by making it return a (new) instance of WorldTile rather than an int:
def __add__(self, other):
return WorldTile(self.resource + other)
This will work for both + and += (handling self.objects is left as an exercise for the reader).
I have a tuple of python objects, from which I need a list of objects with no duplicates, using set() (this check for duplicate objects is to be done on an attribute.). This code will give a simple illustration:
class test:
def __init__(self, t):
self.t = t
def __repr__(self):
return repr(self.t)
def __hash__(self):
return self.t
l = (test(1), test(2), test(-1), test(1), test(3), test(2))
print l
print set(l)
However, it did not work. I can do it on an iteration over l, but any idea why set() is not working? Here is the official documentation.
From the documentation you linked to:
The set classes are implemented using dictionaries. Accordingly, the
requirements for set elements are the same as those for dictionary
keys; namely, that the element defines both __eq__() and __hash__().
To be more specific, if a == b then your implementation must be such that hash(a) == hash(b). The reverse is not required.
Also, you should probably call hash in __hash__ to handle long integers
class Test:
def __init__(self, t):
self.t = t
def __repr__(self):
return repr(self.t)
def __hash__(self):
return hash(self.t)
def __eq__(self, other):
return isinstance(other, Test) and self.t == other.t
Small nit picks:
Your implementation of __eq__ doesn't give the other object a chance to run its own __eq__. The class must also consider its members as immutable as the hash must stay constant. You don't want to break your dicts, do you?
class Test:
def __init__(self, t):
self._t = t
#property
def t(self):
return self._t
def __repr__(self):
return repr(self._t)
def __hash__(self):
return hash(self._t)
def __eq__(self, other):
if not isinstance(other, Test):
return NotImplemented # don't know how to handle `other`
return self.t == other.t
I'd like to implement an object, that bounds values within a given range after arithmetic operations have been applied to it. The code below works fine, but I'm pointlessly rewriting the methods. Surely there's a more elegant way of doing this. Is a metaclass the way to go?
def check_range(_operator):
def decorator1(instance,_val):
value = _operator(instance,_val)
if value > instance._upperbound:
value = instance._upperbound
if value < instance._lowerbound:
value = instance._lowerbound
instance.value = value
return Range(value, instance._lowerbound, instance._upperbound)
return decorator1
class Range(object):
'''
however you add, multiply or divide, it will always stay within boundaries
'''
def __init__(self, value, lowerbound, upperbound):
'''
#param lowerbound:
#param upperbound:
'''
self._lowerbound = lowerbound
self._upperbound = upperbound
self.value = value
def init(self):
'''
set a random value within bounds
'''
self.value = random.uniform(self._lowerbound, self._upperbound)
def __str__(self):
return self.__repr__()
def __repr__(self):
return "<Range: %s>" % (self.value)
#check_range
def __mul__(self, other):
return self.value * other
#check_range
def __div__(self, other):
return self.value / float(other)
def __truediv__(self, other):
return self.div(other)
#check_range
def __add__(self, other):
return self.value + other
#check_range
def __sub__(self, other):
return self.value - other
It is possible to use a metaclass to apply a decorator to a set of function names, but I don't think that this is the way to go in your case. Applying the decorator in the class body on a function-by-function basis as you've done, with the #decorator syntax, I think is a very good option. (I think you've got a bug in your decorator, BTW: you probably do not want to set instance.value to anything; arithmetic operators usually don't mutate their operands).
Another approach I might use in your situation, kind of avoiding decorators all together, is to do something like this:
import operator
class Range(object):
def __init__(self, value, lowerbound, upperbound):
self._lowerbound = lowerbound
self._upperbound = upperbound
self.value = value
def __repr__(self):
return "<Range: %s>" % (self.value)
def _from_value(self, val):
val = max(min(val, self._upperbound), self._lowerbound)
# NOTE: it's nice to use type(self) instead of writing the class
# name explicitly; it then continues to work if you change the
# class name, or use a subclass
return type(self)(val, rng._lowerbound, rng._upperbound)
def _make_binary_method(fn):
# this is NOT a method, just a helper function that is used
# while the class body is being evaluated
def bin_op(self, other):
return self._from_value(fn(self.value, other))
return bin_op
__mul__ = _make_binary_method(operator.mul)
__div__ = _make_binary_method(operator.truediv)
__truediv__ = __div__
__add__ = _make_binary_method(operator.add)
__sub__ = _make_binary_method(operator.sub)
rng = Range(7, 0, 10)
print rng + 5
print rng * 50
print rng - 10
print rng / 100
printing
<Range: 10>
<Range: 10>
<Range: 0>
<Range: 0.07>
I suggest that you do NOT use a metaclass in this circumstance, but here is one way you could. Metaclasses are a useful tool, and if you're interested, it's nice to understand how to use them for when you really need them.
def check_range(fn):
def wrapper(self, other):
value = fn(self, other)
value = max(min(value, self._upperbound), self._lowerbound)
return type(self)(value, self._lowerbound, self._upperbound)
return wrapper
class ApplyDecoratorsType(type):
def __init__(cls, name, bases, attrs):
for decorator, names in attrs.get('_auto_decorate', ()):
for name in names:
fn = attrs.get(name, None)
if fn is not None:
setattr(cls, name, decorator(fn))
class Range(object):
__metaclass__ = ApplyDecoratorsType
_auto_decorate = (
(check_range,
'__mul__ __div__ __truediv__ __add__ __sub__'.split()),
)
def __init__(self, value, lowerbound, upperbound):
self._lowerbound = lowerbound
self._upperbound = upperbound
self.value = value
def __repr__(self):
return "<Range: %s>" % (self.value)
def __mul__(self, other):
return self.value * other
def __div__(self, other):
return self.value / float(other)
def __truediv__(self, other):
return self / other
def __add__(self, other):
return self.value + other
def __sub__(self, other):
return self.value - other
As it is wisely said about metaclasses: if you wonder wether you need them, then you don't.
I don't fully understand your problem, but I would create a BoundedValue class, and us only instances of said class into the class you are proposing.
class BoundedValue(object):
default_lower = 0
default_upper = 1
def __init__(self, upper=None, lower=None):
self.upper = upper or BoundedValue.default_upper
self.lower = lower or BoundedValue.default_lower
#property
def val(self):
return self._val
#val.setter
def val(self, value):
assert self.lower <= value <= self.upper
self._val = value
v = BoundedValue()
v.val = 0.5 # Correctly assigns the value 0.5
print v.val # prints 0.5
v.val = 10 # Throws assertion error
Of course you could (and should) change the assertion for the actual behavior you are looking for; also you can change the constructor to include the initialization value. I chose to make it an assignment post-construction via the property val.
Once you have this object, you can create your classes and use BoundedValue instances, instead of floats or ints.