Question

Lorsque vous utilisez la compréhension de la liste ou le mot-clé in dans un contexte pour la boucle, i.e.:

for o in X:
    do_something_with(o)

ou

l=[o for o in X]
  • Comment le mécanisme sous-jacent fonctionne in?
  • Quelles méthodes de fonctions au sein X faut-il appeler?
  • Si X peut répondre à plus d'une méthode, quelle est la priorité?
  • Comment écrire un X efficace, de sorte que la compréhension de la liste sera rapide?
Était-ce utile?

La solution

, autant que je sache, réponse complète et correcte.

for, aussi bien dans les boucles et compréhensions de la liste, les appels iter() sur X. iter() retourne un iterable si X a soit une méthode de __iter__ ou un procédé de __getitem__. Si elle met en œuvre à la fois, __iter__ est utilisé. Si elle n'a ni vous obtenir TypeError: 'Nothing' object is not iterable.

Cela représente un __getitem__:

class GetItem(object):
    def __init__(self, data):
        self.data = data

    def __getitem__(self, x):
        return self.data[x]

Utilisation:

>>> data = range(10)
>>> print [x*x for x in GetItem(data)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Ceci est un exemple de mise en œuvre __iter__:

class TheIterator(object):
    def __init__(self, data):
        self.data = data
        self.index = -1

    # Note: In  Python 3 this is called __next__
    def next(self):
        self.index += 1
        try:
            return self.data[self.index]
        except IndexError:
            raise StopIteration

    def __iter__(self):
        return self

class Iter(object):
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        return TheIterator(data)

Utilisation:

>>> data = range(10)
>>> print [x*x for x in Iter(data)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Comme vous le voyez, vous avez besoin à la fois de mettre en œuvre un itérateur et __iter__ qui retourne le iterator.

Vous pouvez les combiner:

class CombinedIter(object):
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        self.index = -1
        return self

    def next(self):
        self.index += 1
        try:
            return self.data[self.index]
        except IndexError:
            raise StopIteration

Utilisation:

>>> well, you get it, it's all the same...

Mais alors vous ne peut avoir qu'un seul itérateur va à la fois. OK, dans ce cas, vous pouvez simplement faire ceci:

class CheatIter(object):
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        return iter(self.data)

Mais ce triche parce que vous êtes juste réutilisez la méthode __iter__ de list. Un moyen plus facile est de rendement d'utilisation, et faire __iter__ dans un générateur:

class Generator(object):
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        for x in self.data:
            yield x

Ce dernier est la façon dont je recommande. Facile et efficace.

Autres conseils

X doit être itérable. Il doit mettre en œuvre __iter__() qui renvoie un objet iterator; l'objet iterator doit mettre en œuvre next(), qui retourne à chaque fois prochain point il est appelé ou soulève un StopIteration s'il n'y a aucun élément suivant.

Les listes, les tuples et les générateurs sont itérables.

Notez que l'opérateur for ordinaire utilise le même mécanisme.

Répondre aux commentaires de question, je peux dire que la source de lecture n'est pas la meilleure idée dans ce cas. Le code qui est responsable de l'exécution du code compilé ( CEVAL. c ) ne semble pas être très bavard pour une personne qui voit les sources Python pour la première fois. Voici l'extrait cette itération représente dans les boucles:

   TARGET(FOR_ITER)
        /* before: [iter]; after: [iter, iter()] *or* [] */
        v = TOP();

        /*
          Here tp_iternext corresponds to next() in Python
        */
        x = (*v->ob_type->tp_iternext)(v); 
        if (x != NULL) {
            PUSH(x);
            PREDICT(STORE_FAST);
            PREDICT(UNPACK_SEQUENCE);
            DISPATCH();
        }
        if (PyErr_Occurred()) {
            if (!PyErr_ExceptionMatches(
                            PyExc_StopIteration))
                break;
            PyErr_Clear();
        }
        /* iterator ended normally */
        x = v = POP();
        Py_DECREF(v);
        JUMPBY(oparg);
        DISPATCH();

Pour ce qui se passe réellement ici, vous devez plonger dans des tas d'autres fichiers qui verbosité est pas beaucoup mieux. Ainsi, je pense que dans ce cas, la documentation et des sites comme SO sont le premier endroit où aller alors que la source doit être vérifiée que pour les détails de mise en œuvre non couvertes.

X doit être un objet itérable, ce qui signifie qu'il doit avoir une méthode __iter__().

Donc, pour commencer une boucle de for..in, ou une compréhension de la liste, la méthode X de premier __iter__() est appelé pour obtenir un objet iterator; alors que la méthode de next() d'objet est appelé pour chaque itération jusqu'à ce que StopIteration est élevée, auquel cas l'itération se bloque.

Je ne suis pas sûr de ce que votre troisième moyen d'interrogation, et la façon de fournir une réponse significative à votre quatrième question, sauf que votre iterator ne devrait pas construire la liste entière en mémoire à la fois.

Peut-être que cela aide (tutoriel http://docs.python.org/tutorial/classes.html Section 9.9):

Dans les coulisses, l'instruction for demande iter () sur l'objet conteneur. La fonction retourne un itérateur objet qui définit la méthode suivante () qui accède à des éléments de la contenant un à la fois. Lorsqu'il ya n'y a pas plus d'éléments, à côté () soulève une StopIteration exception qui indique la boucle pour terminer.

Pour répondre à vos questions:

Comment fonctionne le mécanisme sous-jacent dans les travaux?

Il est le mécanisme exact même que celui utilisé pour les boucles ordinaires, comme d'autres l'ont déjà noté.

Quelles méthodes de fonctions au sein de X ne il appelle?

Comme indiqué dans un commentaire ci-dessous, il appelle iter(X) d'obtenir un itérateur. Si X a une fonction de méthode __iter__() définie, ce sera appelé à retourner un itérateur; Par ailleurs, si X définit __getitem__(), ce sera appelé à plusieurs reprises à itérer sur X. Consultez la documentation Python pour iter() ici: http://docs.python.org/library/ functions.html # iter

Si X peut répondre à plus d'une méthode, quelle est la priorité?

Je ne suis pas sûr de ce que votre question est là, exactement, mais Python a des règles standard pour la façon dont il résout les noms de méthode, et ils sont suivis ici. Voici une discussion de ceci:

Méthode ordre de résolution (MRO) dans le nouveau type classes Python

Comment écrire un X efficace, de sorte que la compréhension de la liste sera rapide?

Je vous suggère de lire jusqu'à plus sur itérateurs et générateurs en Python. Un moyen facile de faire une itération de soutien de classe est de faire fonction de générateur pour iter (). Voici une discussion des générateurs:

http://linuxgazette.net/100/pramode.html

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top