I'm trying to extend some "base" classes in Python:
class xlist (list):
def len(self):
return len(self)
def add(self, *args):
self.extend(args)
return None
class xint (int):
def add(self, value):
self += value
return self
x = xlist([1,2,3])
print x.len() ## >>> 3 ok
print x ## >>> [1,2,3] ok
x.add (4, 5, 6)
print x ## >>> [1,2,3,4,5,6] ok
x = xint(10)
print x ## >>> 10 ok
x.add (2)
print x ## >>> 10 # Not ok (#1)
print type(x) ## >>> <class '__main__.xint'> ok
x += 5
print type(x) ## >>> <type 'int'> # Not ok (#2)
It works fine in the list case because the append method modifies the object "in place", without returning it. But in the int case, the add method doesn't modify the value of the external x variable. I suppose that's fine in the sense that self is a local variable in the add method of the class, but this is preventing me from modifying the initial value assigned to the instance of the class.
Is it possible to extend a class this way or should I define a class property with the base type and map all the needed methods to this property?
Your two xint examples don't work for two different reasons.
The first doesn't work because self += value is equivalent to self = self + value which just reassigns the local variable self to a different object (an integer) but doesn't change the original object. You can't really get this
>>> x = xint(10)
>>> x.add(2)
to work with a subclass of int since integers are immutable.
To get the second one to work you can define an __add__ method, like so:
class xint(int):
def __add__(self, value):
return xint(int.__add__(self, value))
>>> x = xint(10)
>>> type(x)
<class '__main__.xint'>
>>> x += 3
>>> x
13
>>> type(x)
<class '__main__.xint'>
int is a value type, so each time you do an assignment, (e.g. both instances of += above), it doesn't modify the object you have on the heap, but replaces the reference with one of the result of the right hand side of the assignment (i.e. an int)
list isn't a value type, so it isn't bound by the same rules.
this page has more details on the differences: The Python Language Reference - 3. Data model
IMO, yes, you should define a new class that keeps an int as an instance variable
i expanded you xlist class just a bit, made it so you could find all index points of a number making it so you can extend with multiple lists at once making it initialize and making it so you can iterate through it
class xlist:
def __init__(self,alist):
if type(alist)==type(' '):
self.alist = [int(i) for i in alist.split(' ')]
else:
self.alist = alist
def __iter__(self):
i = 0
while i<len(self.alist):
yield self.alist[i]
i+=1
def len(self):
return len(self.alist)
def add(self, *args):
if type(args[0])==type([1]):
if len(args)>1:
tmp = []
[tmp.extend(i) for i in args]
args = tmp
else:args = args[0]
if type(args)==type(''):args = [int(i) for i in args.split(' ')]
(self.alist).extend(args)
return None
def index(self,val):
gen = (i for i,x in enumerate(self.alist) if x == val)
return list(gen)
Ints are immutable and you can't modify them in place, so you should go with option #2 (because option #1 is impossible without some trickery).
I wrote an example of a mutable integer class that implements some basic methods from the list of operator methods. It can print properly, add, subtract, multiply, divide, sort, and compare equality.
If you want it to do everything an int can you'll have to implement more methods.
class MutablePartialInt:
def __init__(self, value):
self.value = value
def _do_relational_method(self, other, method_to_run):
func = getattr(self.value, method_to_run)
if type(other) is MutablePartialInt:
return func(other.value)
else:
return func(other)
def __add__(self, other):
return self._do_relational_method(other, "__add__")
def __sub__(self, other):
return self._do_relational_method(other, "__sub__")
def __mul__(self, other):
return self._do_relational_method(other, "__mul__")
def __truediv__(self, other):
return self._do_relational_method(other, "__truediv__")
def __floordiv__(self, other):
return self._do_relational_method(other, "__floordiv__")
def __eq__(self, other):
return self._do_relational_method(other, "__eq__")
def __neq__(self, other):
return self._do_relational_method(other, "__neq__")
def __lt__(self, other):
return self._do_relational_method(other, "__lt__")
def __gt__(self, other):
return self._do_relational_method(other, "__gt__")
def __str__(self):
return str(self.value)
def __repr__(self):
return self.__str__()
Related
I have a class like so:
class NumberGenerator(object):
def __init__(self, seed=0):
random.seed(seed)
self.current = random.random()
def __next__(self):
self.next()
def next(self):
self.current = random.random()
return self.current
Ideally, I would like to do the following via some magic method if available.
>>> numGen = NumberGenerator(99)
>>> numGen.next()
0.15
>>> numGen + 2
2.15
I know __str__ and __repr__ and __add__ etc. But what I want is a magic method to return something whenever the object is referenced other than a pointer to the object. I will not assign another variable to the object reference, downstream, I promise. Is this available in Python?
The Python language doesn't include pointers. You can think of variables in Python as references to some object. When using a variable, you're accessing the object the variable is referring to.
The behavior you want your NumberGenerator class to have, can be implemented by implementing the __add__ magic method. This is the method Python calls when the + operator is being used with an instance of your class on the left side of the expression. In other words:
numGen + 2
Is translated to:
numGen.__add__(2)
Note that if you want to use the + operator regardless of which side of the expression your class instance is on, you need to also implement the __radd__ magic method:
class NumberGenerator(object):
def __init__(self, seed=0):
random.seed(seed)
self.current = random.random()
def __next__(self):
self.next()
def next(self):
self.current = random.random()
return self.current
def __add__(self, value):
return self.current + value
def __radd__(self, value):
return self.__add__(value)
Here is an example of the class being used:
>>> numGen = NumberGenerator(99)
>>> numGen.next()
0.20007544457494542
>>> numGen + 2
2.2000754445749453
>>> 2 + numGen
2.2000754445749453
>>>
I have the following code:
class Stat(list):
def __init__(self, lst = []):
self.s = list(lst)
def __repr__(self):
return "Stat({})".format(self.s)
def add(self, item):
self.s.append(item)
def len(self):
return len(self.s)
...(more methods, but not necessary)
All of the methods work properly but len(). No matter the length of the Stat object, the returned length is always 0; I don't understand why.
it will return 0 always when you are using it like this:
x = Stat([1,3,4,5])
print len(x)
if you want to override len function use this code:
def __len__(self):
return len(self.s)
s = Stat([1, 2])
s.add(1)
s.add(2)
print s.len()
I have run your code, the result is correct in my environment.
Override the magic method __len__(self) to control the output of a call to len(my_stat_object):
class Stat(list):
def __init__(self, lst = []):
self.s = list(lst)
def __repr__(self):
return "Stat({})".format(self.s)
def add(self, item):
self.s.append(item)
def __len__(self):
return len(self.s)
If what you're trying to run is len(stat) and not stat.len(), your function len should be named __len__ instead. Here's the docs: object.len
stat = Stat([1, 2])
len(s) # 0 if len, 2 if __len__
As a side note, you might want to replace lst=[] in your init definition, as it can cause some weird looking behaviours. Read about it here: mutable default argument
What is a nice way to make different variables refer to the same value, while still allowing direct operations like e.g. * on the value?
Example of desired code is being able to do something like:
a = <Reference to integer 2>
b = a
print(a * b) # Should show 4
<a update (not with assign using =) with reference to integer 3>
print(a * b) # Should show 9
A less desired solution is to use a container for the value, like namespace, list, dict, etc., but this requires reference to the attribute like .value below, so is less desired:
import types
a = types.SimpleNamespace(value = 2)
b = a
print(a.value * b.value) # Should show 4
a.value = 3
print(a.value * b.value) # Should show 9
What is a nice way to encapsulate the value, so direct operations is still possible?
You could create a class which overrides the multiply operation.
class Reference:
def __init__(self, value):
self.value = value
def __mul__(self, other):
return Reference(self.value * other.value)
This will allow you to multiply references by one another directly. For example, Reference(3) * Reference(4) produces Reference(12).
You'll probably want to override __rmul__ and all the other numerical operations as well. The abstract classes in numbers may prove useful to ensure you don't forget any.
Your desired behaviour can be simulated with a class, although a bit clunky and inelegant:
class reference:
def __init__(self, num): self.num = num
def get(self): return self.num
def set(self, num): self.num = num
def __mul__(self, other): return self.num * other
def __div__(self, other): return self.num / other
def __add__(self, other): return self.num + other
def __sub__(self, other): return self.num - other
With these operators overloaded, the following:
a = reference(5)
b = a
print a.get()
print a * 4
prints
5
20
I realise this is quite cumbersome if you want to reference different types, as you would have to overload the operators you need for every type, but AFAIK it's the closest you'll get to simulating pointers.
Alternatively, you can include only get, set and __init__ in your reference class, then add the overloading functions you need later:
class reference:
def __init__(self, num): self.num = num
def get(self): return self.num
def set(self, num): self.num = num
a = reference(5)
reference.__mul__ = lambda self, num: self.num * num
print a * 4
The above prints 20
class Manager:
def __init__(self,data):
self.__dict__["data"] = data
def __getattr__(self,attr):
return getattr(self.data,attr)
def __setattr__(self,attr,val):
return setattr(self.data,attr,val)
def set(self,val):
self.__dict__["data"] = val
master = Manager(55)
print print master+5
print slave = master
print slave.set(88)
print slave + 10
print master+2
...
master_s = Manager("Test")
print master_s + " String"
...
master_c = Manager(MyCustomClass())
master_c.do_my_method()
maybe?
You can use a list around the object:
>>> a = [2]
>>> b = a
>>> print(a[0]*b[0])
4
>>> a[0]+=1
>>> print(a[0]*b[0])
9
To use the container classes, but still allow direct operations, you can overload the operators you wish to use for that type. As an example, define class SharedInt and write an overload for the * operator that takes two SharedInt's as arguments.
__mul__(self, other):
return self.value * other.value
I went through some questions but could not find one that really helps.
Lets say I have a list of objects
[[Cheese(3), Cheese(2), Cheese(1)], []]
and I need to write a function that will find the index of Cheese(1) in there
I tried this:
def location (search):
return self.list.index(Cheese(1))
Which didnt work, I thought list.index(search) returns the index of the searched item in a list?
For the above list the index should have been list[0][2] for Cheese(1)
You need to do two things:
Give your Cheese() class an __eq__ method so that Python knows when two instances hold the same value:
class Cheese(object):
def __init__(self, id):
self.id = id
def __eq__(self, other):
if isinstance(other, Cheese):
return self.id == other.id
return NotImplemented
With this implementation, two Cheese() instances are equal if they have the same id value.
Without __eq__ two references to Cheese() instances are only equal if it concerns the same object (identity).
list.index() does not search nested lists; you need to do so explicitly:
search = Cheese(1)
try:
return next((i, sublist.index(search)) for i, sublist in enumerate(self.list) if search in sublist)
except StopIteration:
raise IndexError('{} not found in the list'.format(Cheese(1))
would return a tuple with 2 indices into the outer and inner lists, representing the first location where Cheese(1) is found.
Demo:
>>> class Cheese(object):
... def __init__(self, id):
... self.id = id
... def __eq__(self, other):
... if isinstance(other, Cheese):
... return self.id == other.id
... return NotImplemented
...
>>> Cheese(1) == Cheese(1)
True
>>> Cheese(1) == Cheese(2)
False
>>> lst = [[Cheese(3), Cheese(2), Cheese(1)], []]
>>> next((i, sublist.index(Cheese(1))) for i, sublist in enumerate(lst) if Cheese(1) in sublist)
(0, 2)
Borrowing the documentation from the __contains__ documentation
print set.__contains__.__doc__
x.__contains__(y) <==> y in x.
This seems to work fine for primitive objects such as int, basestring, etc. But for user-defined objects that define the __ne__ and __eq__ methods, I get unexpected behavior. Here is a sample code:
class CA(object):
def __init__(self,name):
self.name = name
def __eq__(self,other):
if self.name == other.name:
return True
return False
def __ne__(self,other):
return not self.__eq__(other)
obj1 = CA('hello')
obj2 = CA('hello')
theList = [obj1,]
theSet = set(theList)
# Test 1: list
print (obj2 in theList) # return True
# Test 2: set weird
print (obj2 in theSet) # return False unexpected
# Test 3: iterating over the set
found = False
for x in theSet:
if x == obj2:
found = True
print found # return True
# Test 4: Typcasting the set to a list
print (obj2 in list(theSet)) # return True
So is this a bug or a feature?
For sets and dicts, you need to define __hash__. Any two objects that are equal should hash the same in order to get consistent / expected behavior in sets and dicts.
I would reccomend using a _key method, and then just referencing that anywhere you need the part of the item to compare, just as you call __eq__ from __ne__ instead of reimplementing it:
class CA(object):
def __init__(self,name):
self.name = name
def _key(self):
return type(self), self.name
def __hash__(self):
return hash(self._key())
def __eq__(self,other):
if self._key() == other._key():
return True
return False
def __ne__(self,other):
return not self.__eq__(other)
This is because CA doesn't implement __hash__
A sensible implementation would be:
def __hash__(self):
return hash(self.name)
A set hashes it's elements to allow a fast lookup. You have to overwrite the __hash__ method so that a element can be found:
class CA(object):
def __hash__(self):
return hash(self.name)
Lists don't use hashing, but compare each element like your for loop does.