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
Related
Edit: the problem is more complicated than the first question I asked, so I deleted everything and re-edit the question.
I tried to make a class in python, the following code works:
From my test_loop.pyd file
class mockClass:
def __init__(self,val):
self._val = val
def __getattr__(self,attrName):
print('Getattr:',attrName)
print('This work on python:',self.__dict__)
try:
return self.__dict__[attrName]
except:
raise AttributeError("Unknow ",attrName)
def __add__(self,val):
return mockClass(self._val+val)
def __str__(self):
return str(self._val)
def test_func():
print('In test func')
myList = [1,2,3]
for ii,item in enumerate(myList):
if (ii==0):
out = mockClass(item)
else:
print('Add:',item)
out += item
return out
And the module works, my test.py file:
import test_loop
a = test_loop.test_func()
print(a)
But when I change the python class to cython class I got stuck in the recursive loop: the getattr (I tried getattribute too) get stuck because it try to find dict, which is, again, require function getattr.
cdef class mockClass:
cdef int _val
def __init__(self,val):
self._val = val
def __getattr__(self,attrName):
print('Getattr:',attrName)
print('This work on python:',self.__dict__)
try:
return self.__dict__[attrName]
except:
raise AttributeError("Unknow ",attrName)
def __add__(self,val):
return mockClass(self._val+val)
def __str__(self):
return str(self._val)
I can't find any functional getattr example of cython extension class.
Why don't you use a the built-in sum function?
mySym = sum(myList)
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
>>>
So I get this error when I try to get the len() of my list from my class:
TypeError: object of type 'Stuff' has no len()
when I try:
>>> s = Stuff()
>>> len(s)
error instead of showing 0 like:
>>> l = []
>>> len(l)
0
Code:
class Stuff():
def __init__(self):
self.lst = []
Define the __len__ special method like so:
class Stuff():
def __init__(self):
self.lst = []
def __len__(self):
return len(self.lst)
Now you can call it like this:
>>> s = Stuff()
>>> len(s)
0
Use the __len__ special method
class Stuff(object):
def __init__(self):
self.bits = []
def __len__(self):
return len(self.bits)
I highly recommend reading through the docs page on special methods.
http://docs.python.org/2/reference/datamodel.html#special-method-names
There are a fair number of neat things you can do, such as defining how methods such as
Stuff() += 3 or Stuff()[4] behaves.
The argument to len can be "a sequence (string, tuple or list) or a mapping (dictionary)": http://docs.python.org/2/library/functions.html#len
It cannot simply be an object, unless that object explicitly defines a __len__ method. This is the method that implicitly gets called by the len function.
I want a python list which represents itself externally as an average of its internal list items, but otherwise behaves as a list. It should raise a TypeError if an item is added that can't be cast to a float.
The part I'm stuck on is raising TypeError. It should be raised for invalid items added via any list method, like .append, .extend, +=, setting by slice, etc.
Is there a way to intercept new items added to the list and validate them?
I tried re-validating the whole list in __getattribute__, but when its called I only have access to the old version of the list, plus it doesn't even get called initialization, operators like +=, or for slices like mylist[0] = 5.
Any ideas?
Inherit from MutableSequence and implement the methods it requires as well as any others that fall outside of the scope of Sequences alone -- like the operators here. This will allow you to change the operator manipulations for list-like capabilities while automatically generating iterators and contains capabilities.
If you want to check for slices btw you need to do isinstance(key, slice) in your __getitem__ (and/or __setitem__) methods. Note that a single index like myList[0] is not a slice request, but a single index and myList[:0] is an actual slice request.
The array.array class will take care of the float part:
class AverageList(array.array):
def __new__(cls, *args, **kw):
return array.array.__new__(cls, 'd')
def __init__(self, values=()):
self.extend(values)
def __repr__(self):
if not len(self): return 'Empty'
return repr(math.fsum(self)/len(self))
And some tests:
>>> s = AverageList([1,2])
>>> s
1.5
>>> s.append(9)
>>> s
4.0
>>> s.extend('lol')
Traceback (most recent call last):
File "<pyshell#117>", line 1, in <module>
s.extend('lol')
TypeError: a float is required
Actually the best answer may be: don't.
Checking all objects as they get added to the list will be computationally expensive. What do you gain by doing those checks? It seems to me that you gain very little, and I'd recommend against implementing it.
Python doesn't check types, and so trying to have a little bit of type checking for one object really doesn't make a lot of sense.
There are 7 methods of the list class that add elements to the list and would have to be checked. Here's one compact implementation:
def check_float(x):
try:
f = float(x)
except:
raise TypeError("Cannot add %s to AverageList" % str(x))
def modify_method(f, which_arg=0, takes_list=False):
def new_f(*args):
if takes_list:
map(check_float, args[which_arg + 1])
else:
check_float(args[which_arg + 1])
return f(*args)
return new_f
class AverageList(list):
def __check_float(self, x):
try:
f = float(x)
except:
raise TypeError("Cannot add %s to AverageList" % str(x))
append = modify_method(list.append)
extend = modify_method(list.extend, takes_list=True)
insert = modify_method(list.insert, 1)
__add__ = modify_method(list.__add__, takes_list=True)
__iadd__ = modify_method(list.__iadd__, takes_list=True)
__setitem__ = modify_method(list.__setitem__, 1)
__setslice__ = modify_method(list.__setslice__, 2, takes_list=True)
The general approach would be to create your own class inheriting vom list and overwriting the specific methods like append, extend etc. This will probably also include magic methods of the Python list (see this article for details: http://www.rafekettler.com/magicmethods.html#sequence).
For validation, you will need to overwrite __setitem__(self, key, value)
Here's how to create a subclass using the MutableSequence abstract base class in the collections module as its base class (not fully tested -- an exercise for the reader ;-):
import collections
class AveragedSequence(collections.MutableSequence):
def _validate(self, x):
try: return float(x)
except: raise TypeError("Can't add {} to AveragedSequence".format(x))
def average(self): return sum(self._list) / len(self._list)
def __init__(self, arg): self._list = [self._validate(v) for v in arg]
def __repr__(self): return 'AveragedSequence({!r})'.format(self._list)
def __setitem__(self, i, value): self._list[i] = self._validate(value)
def __delitem__(self, i): del self._list[i]
def insert(i, value): return self._list.insert(i, self._validate(value))
def __getitem__(self, i): return self._list[i]
def __len__(self): return len(self._list)
def __iter__(self): return iter(self._list)
def __contains__(self, item): return item in self._list
if __name__ == '__main__':
avgseq = AveragedSequence(range(10))
print avgseq
print avgseq.average()
avgseq[2] = 3
print avgseq
print avgseq.average()
# ..etc
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__()