I'd like to have an ability to reset an integer/float to the predefined default value without overriding all arithmetic operations. Something like
class DefaultInt(int):
def __init__(self, value):
super(DefaultInt, self).__init__(value)
self.default_value = value
def reset(self):
self.value = self.default_value
my_int = DefaultInt(19)
my_int += 1
my_int.reset()
But there are two problems:
I cannot access the hidden value itself by subclassing int class.
After my_int += 1 the my_int becomes, obviously, a simple int.
Your immediate problems:
in-place addition needs you to redefine __iadd__ (in-place addition) to return a DefaultInt object (better save the default value, else it becomes the new value)
The reset thing looks not possible as you've written, just because integers are immutable. But you could assign back the result of reset to the same name. That would work.
class DefaultInt(int):
def __init__(self,value=0):
super(DefaultInt, self).__init__()
self.default_value = value
def __iadd__(self,value):
old_default = self.default_value
r = DefaultInt(value + self)
r.default_value = old_default
return r
def reset(self):
return DefaultInt(self.default_value)
my_int = DefaultInt(19)
my_int += 1
print(my_int)
my_int = my_int.reset()
print(my_int)
output:
20
19
Your long-term problems:
But that's a first step. If you try my_int + 12 you'll see that it returns an int as well: you'll have to define __add__. Same goes for __sub__... and there's the "hidden" value problem, and the immutable problem which prevents you to perform an in-place reset.
Conclusion:
I think the best approach would be not to inherit int and create your own, mutable, class with all the special methods crafted for your needs (plus the reset method that would work now). You won't have the constraints you're having when overriding int, and your code will be clearer, even if method-exhaustive (at least if a method is missing, you'll notice it, instead of calling a method that doesn't fit).
I think that, if you only need a reset function, you can simply use:
default_value = 5
my_int = int.__new__(int, default_value)
my_int += 1
print my_int # prints 6
my_int = int.__new__(int, default_value)
print my_int # prints 5
Related
I want to inherit from integers and only redefine some methods.
The goal is to have this behaviour:
>>> i = Iter()
>>> i == 0
True
>>> next(i)
Iter<1>
>>> next(i)
Iter<2>
>>> i + 10
12
The naive approach would be to inherit from int:
class Iter(int):
def __new__(cls, start=0, increment=1):
return super().__new__(cls, start)
def __init__(self, start=0, increment=1):
self.increment = increment
def __repr__(self):
return f"Iter<{int(self)}>"
def __next__(self):
self += self.increment # DO NOT WORK
return self.value
Unfortunately, int is immutable. I tried to use the ABC for integers, but I don't really want to redefine all operators:
from numbers import Integral
class Iter(Integral):
...
i = Iter()
TypeError: Can't instantiate abstract class I with abstract methods
__abs__, __add__, __and__, __ceil__, __eq__, __floor__,
__floordiv__, __int__, __invert__, __le__, __lshift__, __lt__,
__mod__, __mul__, __neg__, __or__, __pos__, __pow__, __radd__,
__rand__, __rfloordiv__, __rlshift__, __rmod__, __rmul__, __ror__,
__round__, __rpow__, __rrshift__, __rshift__, __rtruediv__,
__rxor__, __truediv__, __trunc__, __xor__
Any other ideas?
Are you aware that itertools.count exists? It does most of what you are trying to do, except for being able to use the instance itself as an integer.
It is not possible to extend either int or itertools.count for this purpose.
Regarding the methods for operators, the reason you would need to define them is because there is not an obvious return value in this case, even if extending int or count worked. What would __add__ return - an integer, or another instance of Iter with its value incremented by the given amount? You would need to decide that, and implement them for your use case.
Rather than extending any existing builtin class, it may be easier to define your own. You can define the __int__ method to control what happens when int(...) is called on an instance of your class.
Example implementation:
class Iter:
def __init__(self, start: int = 0, increment: int = 1):
self._value = start
self._increment = increment
def __int__(self) -> int:
return self._value
def __repr__(self) -> str:
return f'Iter<{self._value}>'
def __next__(self) -> int:
self._value += self._increment
return self._value
Example use:
>>> i = Iter()
>>> next(i)
1
>>> next(i)
2
>>> i
Iter<2>
>>> int(i)
2
I'm studying for a python course and one of the exercise was to create a decorator for this class that returns every int variables.`
#decoratoreDiClasse
class MyClass:
def __init__(self):
self.a = 1
self.b = 2
self.c = 'w'`
My problem is that the list is always empty beacuse dict does not return the variables inside init,how can i solve my problem?
i've written my decorator below
def decoratoreDiClasse(cls):
def elencaVariabili():
lista = []
print(cls)
lista1 = cls.__dict__
print(lista1)
for ab in lista1:
if isinstance(ab, int):
lista.append(ab)
return lista
setattr(cls, "elencaVariabili", elencaVariabili())
return cls
here's the part of the main that should print the variables,I cannot change anything apart from "decoratoreDiClasse" due to the teacher request.
for v in x.elencaVariabili():
print(v, end=' ')
It looks like you're supposed to have your decorator add a method to the class that prints out integer-valued attributes on an instance it's called on. That's not what you're currently trying to do, as your code tries to find the variables on the class instead of on an instance later on.
Think of what you're doing as a method, and it will be a lot simpler:
def decoratoreDiClasse(cls):
def elencaVariabili(self): # this is a method, so it should take self!
lista = [value for value in self.__dict__.values() # loop over our attribute values
if isinstance(value, int)] # and pick out the integers!
return lista
setattr(cls, "elencaVariabili", elencaVariabili) # don't call the method here
return cls
It's not entirely clear from your code if you're supposed to be returning the names of the integer variables or just the values themselves. I went with just the values, but if you need the variable names, you may need to change the list comprehension to iterate over the items() of the instance's dictionary rather than just the values().
The main function of the class is a dictionary with words as keys and id numbers as values (note: id is not in sequential because some of the entries are removed):
x = {'foo':0, 'bar':1, 'king':3}
When i wrote the iterator function for a customdict class i created, it breaks when iterating through range(1 to infinity) because of a KeyError.
class customdict():
def __init__(self,dic):
self.cdict = dic
self.inverse = {}
def keys(self):
# this is necessary when i try to overload the UserDict.Mixin
return self.cdict.values()
def __getitem__(self, valueid):
""" Iterator function of the inversed dictionary """
if self.inverse == {}:
self.inverse = {v:k for k,v in self.cdict.items()}
return self.inverse[valueid]
x = {'foo':0, 'bar':1, 'king':3}
y = customdict(x)
for i in y:
print i
Without try and except and accessing the len(x), how could I resolve the iteration of the dictionary within the customdict class? Reason being x is >>>, len(x) will take too long for realtime.
I've tried UserDict.DictMixin and suddenly it works, why is that so?:
import UserDict.DictMixin
class customdict(UserDict.DictMixin):
...
Is there a way so that i don't use Mixin because somehow in __future__ and python3, mixins looks like it's deprecated?
Define following method.
def __iter__(self):
for k in self.keys():
yield k
I've tried UserDict.DictMixin and suddenly it works, why is that so?:
Because DictMixin define above __iter__ method for you.
(UserDict.py source code.)
Just share another way:
class customdict(dict):
def __init__(self,dic):
dict.__init__(self,{v:k for k,v in dic.items()})
x = {'foo':0, 'bar':1, 'king':3}
y = customdict(x)
for i in y:
print i,y[i]
result:
0 foo
1 bar
3 king
def __iter__(self):
return iter(self.cdict.itervalues())
In Python3 you'd call values() instead.
You're correct that UserDict.DictMixin is out of date, but it's not the fact that it's a mixin that's the problem, it's the fact that collections.Mapping and collections.MutableMapping use a more sensible underlying interface. So if you want to update from UserDict.DictMixin, you should switch to collections.Mapping and implement __iter__() and __len__() instead of keys().
Consider the following simple example class, which has a property that exposes a modified version of some internal data when called:
class Foo(object):
def __init__(self, value, offset=0):
self._value = value
self.offset = offset
#property
def value(self):
return self._value + self.offset
#value.setter
def value(self, value):
self._value = value
The value.setter works fine for regular assignment, but of course breaks down for compound assignment:
>>> x = Foo(3, 2)
>>> x.value
5
>>> x.value = 2
>>> x.value
4
>>> x.value += 5
>>> x.value
11
The desired behavior is that x.value += 5 should be equivalent to x.value = x._value + 5, as opposed to x.value = x.value + 5. Is there any way to achieve this (with the property interface) purely within the Foo class?
#ezod, there is no direct way to do what you're asking for, even with the descriptors protocol.
That kind behaviour of value property totally breaks the semantics of += operator.
Override the __iadd__ magic method.
You need to do that because += means "take the value and add five, then set that as the result". If you want it to know that the value isn't really the value, you need to change the semantics of the += operator.
I was confronted with the same problem and solved it with the following method:
class Foo(object):
def __init__(self):
self._y = 0
#property
def y(self):
return self._y + 5
#y.setter
def y(self, value):
value -= 5
self._y = value
>>> f = Foo()
>>> f.y
5
>>> f.y += 1
>>> f._y
1
>>> f.y
6
Could this be a solution? Have I overlooked something?
The desired behavior is that x.value += 5 should be equivalent to
x.value = x._value + 5
Why should Python know how you property is implemented (i.e. that setter assigns its value exactly to _x)?
Protocol of property gives you a possibility assign different actions (get, set, delete) to one name. And this protocol doesn't care about a way of implementation of these actions.
So it would be quite confusable if Python make some assumptions about your code and try to modify strait forward code logic.
A stupid newbie question here
For a python dictionary q len(set(q.keys())) != len(q.keys()). Is that even possible?
This can happen if you violate a requirement of dict, and change its hash.
When an object is used in a dict, its hash value must not change, and its equality to other objects must not change. Other properties may change, as long as they don't affect how it appears to the dict.
(This does not mean that a hash value is never allowed to change. That's a common misconception. Hash values themselves may change. It's only dict which requires that key hashes be immutable, not __hash__ itself.)
The following code adds an object to a dict, then changes its hash out from under the dict. q[a] = 2 then adds a as a new key in the dict, even though it's already present; since the hash value changed, the dict doesn't find the old value. This reproduces the peculiarity you saw.
class Test(object):
def __init__(self, h):
self.h = h
def __hash__(self):
return self.h
a = Test(1)
q = {}
q[a] = 1
a.h = 2
q[a] = 2
print q
# True:
print len(set(q.keys())) != len(q.keys())
The underlying code for dictionaries and sets is substantially the same, so you can usually expect that len(set(d.keys()) == len(d.keys()) is an invariant.
That said, both sets and dicts depend on __eq__ and __hash__ to identify unique values and to organize them for efficient search. So, if those return inconsistent results (or violate the rule that "a==b implies hash(a)==hash(b)", then there is no way to enforce the invariant:
>>> from random import randrange
>>> class A():
def __init__(self, x):
self.x = x
def __eq__(self, other):
return bool(randrange(2))
def __hash__(self):
return randrange(8)
def __repr__(self):
return '|%d|' % self.x
>>> s = [A(i) for i in range(100)]
>>> d = dict.fromkeys(s)
>>> len(d.keys())
29
>>> len(set(d.keys()))
12