J'ai un problème avec 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
J'ai déjà vu cette question je retourne un problème personnel mais je ne comprends pas quel est l'avantage de return self
?
Renvoyer self
à partir d'une méthode signifie simplement que votre méthode renvoie une référence à l'objet d'instance sur lequel elle a été appelée. Ceci peut parfois être vu en utilisation avec des API orientées objet conçues comme une interface fluide qui encourage méthode en cascade . Donc, par exemple,
>>> 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()
Nous pouvons maintenant utiliser la méthode en cascade:
>>> c.increment().increment().decrement()
<__main__.Counter object at 0x1020c1390>
Notez que le dernier appel à decrement()
a retourné <__main__.Counter object at 0x1020c1390>
, lequel estself
. Maintenant:
>>> c.val
2
>>>
Notez que vous ne pouvez pas faire cela si vous n'avez pas retourné 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
>>>
Notez que tout le monde n'est pas fan de la conception de "méthode en cascade". Les fonctions intégrées de Python n’ont pas tendance à le faire, alors list
par exemple:
>>> 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'
>>>
Le seul endroit que do voit souvent est le moment où votre classe implémente le protocole iterator
, où iter
sur un itérateur renvoie self
par convention, bien que ceci soit suggéré par la documentation :
Après avoir vu les mécanismes derrière le protocole itérateur, il est facile D’ajouter un comportement d’itérateur à vos classes. Définissez une méthode
__iter__()
Qui renvoie un objet avec une méthode__next__()
. Si la classe Définit__next__()
, alors__iter__()
peut simplement renvoyerself
: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]
Notez que cela rend en fait votre itérateur uniquement utile pour un seul passage:
>>> 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
>>>
C'est un code inutile et complexe. Faites peu attention à cela. Il n'y a aucune raison sur terre de l'appliquer de cette façon.
Cela étant dit, voici ce qu'il fait:
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
Tout cela est beaucoup plus facile à exprimer en tant que:
def fib(max_):
a, b = 0, 1
while b <= max_:
out = a
a, b = b, a+b
yield out
Exemples:
>>> 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