Purpose of return self python - python

I have a problem with return self
class Fib:
def __init__(self, max):
self.max = max
def __iter__(self):
self.a = 0
self.b = 1
return self
def __next__(self):
fib = self.a
if fib > self.max:
raise StopIteration
self.a, self.b = self.b, self.a + self.b
return fib
I have already seen this question return self problem but I can't understand what the benefit is of return self?

Returning self from a method simply means that your method returns a reference to the instance object on which it was called. This can sometimes be seen in use with object oriented APIs that are designed as a fluent interface that encourages method cascading. So, for example,
>>> class Counter(object):
... def __init__(self, start=1):
... self.val = start
... def increment(self):
... self.val += 1
... return self
... def decrement(self):
... self.val -= 1
... return self
...
>>> c = Counter()
Now we can use method cascading:
>>> c.increment().increment().decrement()
<__main__.Counter object at 0x1020c1390>
Notice, the last call to decrement() returned <__main__.Counter object at 0x1020c1390>, which is self.
Now:
>>> c.val
2
>>>
Notice, you cannot do this if you did not return self:
>>> class Counter(object):
... def __init__(self, start=1):
... self.val = start
... def increment(self):
... self.val += 1
... # implicitely return `None`
... def decrement(self):
... self.val -= 1
... # implicitely return `None`
...
>>> c = Counter()
>>> c.increment().increment()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'increment'
>>> c
<__main__.Counter object at 0x1020c15f8>
>>> c.val
2
>>>
Notice, not everyone is a fan of "method cascading" design. Python built-ins do not tend do this, so, list for example:
>>> x = list()
>>> x.append(1).append(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'append'
>>>
The one place you do often see this is when your class implements the iterator protocol, where iter on an iterator returns self by convention, although this is suggested by the docs:
Having seen the mechanics behind the iterator protocol, it is easy to
add iterator behavior to your classes. Define an __iter__() method
which returns an object with a __next__() method. If the class
defines __next__(), then __iter__() can just return self:
class Reverse:
"""Iterator for looping over a sequence backwards."""
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
Notice, this in effect makes your iterator only useful for a single pass (as it should be to properly follow the iterator protocol):
>>> x = [1, 2, 3, 4]
>>> it = iter(x)
>>> list(it)
[1, 2, 3, 4]
>>> list(it)
[]
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>

This is needlessly complex code. Pay little attention to it. There's no reason on earth to implement it this way.
That being said, what it does is this:
class Fib:
"""Implements the Fibonacci sequence."""
def __init__(self, max_):
self.max = max_
def __iter__(self):
"""Initializes and returns itself as an iterable."""
self.a = 0
self.b = 1
return self
def __next__(self):
"""What gets run on each execution of that iterable."""
fib = self.a
if fib > self.max:
raise StopIteration
self.a, self.b = self.b, self.a + self.b # increment
return fib
This is all much easier to express as:
def fib(max_):
a, b = 0, 1
while b <= max_:
out = a
a, b = b, a+b
yield out
Examples:
>>> fib_obj = Fib(20)
>>> for n in fib_obj:
... print(n)
>>> for n in Fib(20):
... print(n)
>>> for n in fib(20):
... print(n)
# all give....
0
1
1
2
3
5
8
13

Related

Pythonic way to control an attribute over another attribute

There is a pythonic way to control attributes?
I wan't that the init method is clear, trying to avoid to use a function like:
self.a = check_a_greater_than_b(a)
and avoid to do controls in the init method
def __init__(self, a, b):
if b > a:
raise AttributeError('A should be > B')
else:
self.a = a
self.b = b
I was think to use the setter or settattr, can you post me an example please?
You can do something like this :
class Object():
def __init__(self, a, b):
self.b = b
self.a = a
#property
def a(self):
return self.__a
#a.setter
def a(self, a):
assert a > self.b, "A should be > B"
self.__a = a
Then you will have :
o = Object(3,2)
o.a = 1
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<input>", line 12, in a
AssertionError: A should be > B

Python class __add__ method with number as first operand [duplicate]

I recently learned about operator overloading in python and I would like to know if the following is possible.
Consider the following hypothetical/contrived class.
class My_Num(object):
def __init__(self, val):
self.val = val
def __add__(self, other_num):
if isinstance(other_num, My_Num):
return self.val + other_num.val
else:
return self.val + other_num
I know that the way that's written above, I can do things like this
n1 = My_Num(1)
n2 = My_Num(2)
n3 = 3
print n1 + n2
print n1 + n3
and those will work as expected. I also know that the way it's currently written I can't do this
n1 = My_Num(1)
n2 = 2
print n2 + n1
Is there any way around this? I know this example is contrived but I have an application in which it would be very useful if when I did operator overloading, the class for which I define the operator can appear on the right hand side of operator. Is this possible in python?
Yes. For example, there is __radd__. Also, there are none for __le__(), __ge__(), etc., but as Joel Cornett rightly observes, if you define only __lt__, a > b calls the __lt__ function of b, which provides a workaround.
>>> class My_Num(object):
... def __init__(self, val):
... self.val = val
... def __radd__(self, other_num):
... if isinstance(other_num, My_Num):
... return self.val + other_num.val
... else:
... return self.val + other_num
...
>>> n1 = My_Num(1)
>>> n2 = 3
>>>
>>> print n2 + n1
4
>>> print n1 + n2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'My_Num' and 'int'
Note that in at least some cases it's reasonable to do something like this:
>>> class My_Num(object):
... def __init__(self, val):
... self.val = val
... def __add__(self, other_num):
... if isinstance(other_num, My_Num):
... return self.val + other_num.val
... else:
... return self.val + other_num
... __radd__ = __add__
You have to overload the __radd__ method (right-side addition). Your function should look pretty much the same as your __add__ method, e.g.:
def __radd__(self, other):
return self.val + other.val

Implement a reversible iteration class to replace a generator

In python, I have a class with a method that returns a generator:
class foo():
data = [1, 2, 3]
def mygen(self):
for d in self.data:
yield d
instance = foo()
print([i for i in instance.mygen()])
But I can't reverse this:
print([i for i in reversed(instance.mygen())])
TypeError: 'generator' object is not reversible
So I thought I could implement a class which returns a generator when calling __iter__, like this
class foo():
data = [1, 2, 3]
def mygen(self):
return _ReversibleIterator(self)
class _ReversibleIterator(object):
def __init__(self, obj):
self.obj = obj
def __iter__(self):
for d in obj.data:
yield d
def __reversed__(self):
for d in reversed(obj.data):
yield d
But I think this isn't quite the same, because the _ReversibleIterator class doesn't have a next() method.
So what is the pythonic way to create a class method that returns an iterator that can be reversed()?
(Obviously I'm just using [1,2,3] as an example. The real thing to iterate over is less trivially reversible)
According to the docs, reversed must have one of two things to work with: a __reversed__ method OR a __len__ and a __getitem__ method. If you think about it, this makes sense because most generators can't support reversed because they generate results on the fly: they don't know what the next, let alone the last element is going to be. However, if you know its length and have random-access to any index, it can be reversed.
class foo():
data = [1, 2, 3]
def mygen(self):
return _ReversibleIterator(self)
class _ReversibleIterator(object):
def __init__(self, obj):
self.obj = obj
self.index = 0
def __iter__(self):
self.index = 0
return self
def __reversed__(self):
return reversed(self.obj.data)
def __next__(self):
try:
el = self.obj.data[self.index]
except IndexError:
raise StopIteration
self.index += 1
return el
or
class _ReversibleIterator(object):
def __init__(self, obj):
self.obj = obj
self.index = 0
def __iter__(self):
self.index = 0
return self
def __len__(self):
return len(self.obj.data)
def __getitem__(self, i):
return self.obj.data[i]
def __next__(self):
try:
el = self[self.index]
except IndexError:
raise StopIteration
self.index += 1
return el
By the way, if you would like, you can replace for d in whatever: yield d with yield from whatever.

immutable objects in Python that can have weak references

I've been subclassing tuple or using namedtuple blissfully for a few years, but now I have a use case where I need a class that can be used as a weak referent. And today I learned tuples don't support weak references.
Is there another way to create an immutable object in Python with a fixed set of attributes? I don't need the numeric indexing or variable width of a tuple.
class SimpleThingWithMethods(object):
def __init__(self, n, x):
# I just need to store n and x as read-only attributes
... ??? ...
I guess this raises the obvious question of why immutable; "Pythonic" code usually just assumes we're all adults here and no one in their right mind would reach into a class and muck with its values if it risks ruining the class invariants. In my case I have a class in a library and I am worried about accidental modification of objects by end-users. The people I work with sometimes make incorrect assumptions about my code and start doing things I did not expect, so it's much cleaner if I can raise an error if they accidentally modify my code.
I'm not so worried about bulletproof immutability; if someone really nefarious wants to go and modify things, ok, fine, they're on their own. I just want to make it hard to accidentally modify my objects.
well, this isn't a great answer but it looks like I can modify the answer in https://stackoverflow.com/a/4828492/44330 --- essentially overriding __setattr__ and __delattr__ to meet my needs at least against accidental modification. (but not as nice as subclassing tuple)
class Point(object):
__slots__ = ('x','y','__weakref__')
def __init__(self, x, y):
object.__setattr__(self, "x", x)
object.__setattr__(self, "y", y)
def __setattr__(self, *args):
raise TypeError
def __delattr__(self, *args):
raise TypeError
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __hash__(self):
return self.x.__hash__() * 31 + self.y.__hash__()
Implementing #Elazar's idea:
class Point(object):
__slots__ = ('x','y','__weakref__')
def __new__(cls, x, y):
thing = object.__new__(cls)
object.__setattr__(thing, "x", x)
object.__setattr__(thing, "y", y)
return thing
def __setattr__(self, *args):
raise TypeError
def __delattr__(self, *args):
raise TypeError
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __hash__(self):
return self.x.__hash__() * 31 + self.y.__hash__()
If you don't worry about isinstance checks, you can strengthen you answer:
def Point(x, y):
class Point(object):
__slots__ = ('x','y','__weakref__')
def __setattr__(self, *args):
raise TypeError
def __delattr__(self, *args):
raise TypeError
def __eq__(self, other):
return x == other.x and y == other.y
def __hash__(self):
return x.__hash__() * 31 + y.__hash__()
p = Point()
object.__setattr__(p, "x", x)
object.__setattr__(p, "y", y)
return p
I don't really recommend it (every invocation creates a class!), just wanted to note the possibility.
It is also possible to go javascript all the way, and supply __getattr__ that will access the local variables. But that will also slow down access, in addition to creation. Now we don't need these slots at all:
class MetaImmutable:
def __setattr__(self, name, val):
raise TypeError
def Point(x, y):
class Point(object):
__metaclass__ = MetaImmutable
__slots__ = ('__weakref__',)
def __getattr__(self, name):
if name == 'x': return x
if name == 'y': return y
raise TypeError
#property
def x(self): return x
#property
def y(self): return y
def __eq__(self, other):
return x == other.x and y == other.y
def __hash__(self):
return x.__hash__() * 31 + y.__hash__()
return Point()
Test it:
>>> p = Point(1, 2)
>>> p.y
2
>>> p.z
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in __getattr__
TypeError
>>> p.z = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Point' object has no attribute 'z'
>>> object.__setattr__(p, 'z', 5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Point' object has no attribute 'z'
>>> from weakref import ref
>>> ref(p)().x
1
>>> type(p).x = property(lambda self: 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __setattr__
TypeError
And finally, you can still break it:
>>> type.__setattr__(type(p), 'x', property(lambda self: 5))
>>> p.x
5
Again, nothing here is recommended. Use #Jasons implementation.
What about using encapsulation and abstraction on the parameter (getter?):
class SimpleThingWithMethods(object):
def __init__(self, n, x):
self._n = n
self._x = x
def x(self):
return self._x
def n(self):
return self._n
SimpleThingWithMethods(2,3).x()
=> 3

"object is not iterable" error on my python implementation of iterable

I am trying to create a iterable class but have been banging my head on a wall so to speak, getting "object is not iterable". This is my code:
class myiterable:
def __init__(self, somelist):
self.i = 0
self.l = somelist
def __iter__(self):
return self
def __next__(self):
if self.i < len(self.l):
self.i = self.i + 1
return self.l[self.i-1]
else:
raise StopIteration
for i in myiterable([1, 2, 3, 4, 5]):
print(i)
What's wrong? I have also tried next(self) in lieu of __next__(self) to no avail!
There are several problems with your code:
indentation
if you are on python 2, you should have defined next() method instead of __next__() (leave it as is if on python 3)
++self.i should be replaced with self.i += 1
self.l[i-1] should be replaced with self.l[self.i-1]
class myiterable:
def __init__(self, somelist):
self.i = 0
self.l = somelist
def __iter__(self):
return self
def next(self):
if self.i < len(self.l):
self.i += 1
return self.l[self.i-1]
else:
raise StopIteration
for i in myiterable([1, 2, 3, 4, 5]):
print(i)
prints:
1
2
3
4
5
If you just copied your code, then it should be because of bad indent. Pull __iter__ and __next__ to same indent as __init__.

Categories

Resources