web-dev-qa-db-fra.com

But du retour auto python

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?

7
AmrElgendy

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 renvoyer 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]

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
>>>
12
juanpa.arrivillaga

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
1
Adam Smith