8
votes

Comment vérifier si une iérente permet plus d'un passage?

En Python 3, comment puis-je vérifier si un objet est un conteneur (au lieu d'un itérateur qui peut permettre à une seule passe)

Voici un exemple: p>

def weighted_average(it):
    '''
    returns weighted average of an iterator it
    it yields values and weights in tuples
    weights don't need to sum up 1 (automatically renormalized)
    '''
    total_value = 0
    total_weight = 0
    for v, w in it:
        total_value += v
        total_weight += w
    return total_value / total_weight


9 commentaires

Je ne vois pas le problème avec la version générale, avez-vous profils? Et qu'entendez-vous par la complexité visuelle?


« Où il est pas évident quand exactement la seconde passe »? Qu'est-ce que cela peut vouloir dire? Vous pouvez utiliser itertools.tee () pour la garantie inconditionnelle que vous pouvez itérer autant de fois que nécessaire. Comment peut-il pas évident quand vous concevez les algorithmes?


@LionelBarret: Je suis d'accord, je suis d'accord, il n'y a aucune raison de ne pas utiliser le général pondéded_oovery . J'ai mis à jour la question pour donner un exemple différent.


@ S. Lott: Je voulais juste dire que si l'algorithme n'est pas linéaire, cela peut se produire à plusieurs endroits dans le code; et il pourrait même ne pas être totalement évident où ces endroits sont (par exemple, si une nouvelle passe est demandée quand une condition est violée). Pensant que une fois est assez mauvais; le faire chaque fois que le algoirhtm est peaufiné est vraiment mauvais. D'où ma préférence est de faire la validation au début.


@max: Vous faites que l'algorithme ne soit pas conçu par une sorte d'arrive spontanément sur la scène. C'est troublant. Pouvez-vous expliquer pourquoi le design ordinaire ne fonctionne pas?


@ S.Lott. Je verrai si je peux parvenir à un exemple simple; Ou bien arriver à la conclusion qu'un algorithme bien écrit nécessitant de multiples passes aura un point clair lorsque chaque nouveau passage commence.


@max: veuillez faire. Le iTERTOOLS.EEE () semble être la solution à votre problème. Je sais aussi qu'une boucle est généralement un problème de conception très grave. Donc, je suis confondu par notre question et vous avez intéressé à voir s'il y a un exemple de crypto-boucle qui n'est pas une partie de première classe de la conception.


@ S.Lott Vous avez raison. Je n'ai pas de bon exemple.


@max: J'ai lu avec le même iTertools.tee en essayant de trouver un moyen de le faire "invisible" pour les algorithmes qui nécessitent deux passes à travers une iérienne. Cela n'a jamais été obscutable ou mystérieux pour moi, il suffit de prendre soin de s'assurer que l'itérateur était "tee-d" correctement. J'étais très curieux de voir comment cela pourrait obtenir plus complexe à travers une boucle qui n'était pas "totalement évidente". Merci d'avoir pensé à cela au lieu de faire ce que certaines personnes font: Soit insister sur le fait que c'est une exigence (quand elle n'est pas ") ou de dire" ils sont juste curieux ". La curiosité ne fait pas une mauvaise question bien.


3 Réponses :


3
votes

Vous pouvez utiliser les classes de base abstraites définies dans le module code> Collections CODE> pour vérifier et voir si IT code> est une instance de collections.itéator.

if isinstance(it, collections.Iterator):
    # handle the iterator case


4 commentaires

Oui je suis d'accord. J'ai mis à jour ma question pour montrer un exemple où il ne semble pas être réalisable d'utiliser une passe.


Joli! On dirait qu'il y a une manière standard en 3.x.


Cela semble fonctionner, même pour les objets tels que le "conteneur virtuel" plage (5) . A l'air super!


Cela ne fonctionne pas pour moi. Si j'exécute a = (x pour x dans [1, 2, 3]) , alors isinstance (a, collections.atiform) , il renvoie vrai < / Code>, même s'il doit renvoyer false car A est quelque chose qui ne peut être itéré sur une fois.



1
votes

Le meilleur moyen serait d'utiliser l'infrastructure de classe de base abstraite: xxx


0 commentaires

4
votes

Bien que tous les itérables soient des collections de sous-classement. Sans toutes les personnes ne le font malheureusement. Voici une réponse basée sur la mise en œuvre de l'interface des objets, au lieu de ce qu'ils "déclarent".

Réponse courte: strong> p>

un "conteneur" à mesure que vous l'appelez, c'est-à-dire une liste / tuple pouvant être itérale sur plus d'une fois par opposition à être un générateur qui sera Être épuisé, mettra typiquement mettre en œuvre les deux __ iter __ code> et __ getItem __ code>. Par conséquent, vous pouvez faire ceci: p> xxx pré>

réponse longue: strong> p>

Cependant, vous pouvez faire une ierérable qui ne sera pas épuisée Et ne supporte pas getItem fort>. Par exemple, une fonction qui génère des nombres premiers. Vous pouvez répéter la génération plusieurs fois si vous le souhaitez, mais avoir une fonction pour récupérer le 1065e Prime prendrait beaucoup de calcul, vous ne voudrez peut-être pas supporter cela. : -) p>

y a-t-il plus de manière "fiable"? p>

Eh bien, tous les itérables implémenteront une fonction __ iTer __ code> qui retournera un itérateur. Les itérateurs auront une fonction __ suivante __ code>. C'est ce qui est utilisé lors de l'itération sur elle. Appelant __ Suivant __ code> sera à plusieurs reprises échapper à l'éruptement à plusieurs reprises l'itérateur. P>

donc si elle a un __ suivant __ code> Il s'agit d'un itérateur et sera épuisé. xxx pré>

itérables qui ne sont pas encore des itérateurs n'auront pas de fonction __ suivant __ code>, mais implémentera un __ iter __ code> fonctionner Renvoie une iérente: p> xxx pré>

afin que vous puissiez vérifier que l'objet a __ iter __ code> mais qu'il n'a pas __ suivant __ code>. p> xxx pré>

itérateurs a également un __ iTer __ code> fonction, qui retournera soi-même. p> xxx pré>

, vous pouvez faire ces variations de la vérification: p> xxx pré>

qui échouerait si vous implémentez un objet qui renvoie un itérateur brisé, celui qui fait pas em> Retournez-vous quand vous appelez iTer () dessus. Mais alors, votre code (ou un tiers modules) fait de mal à faire mal. P>

Cela dépend de la création d'un itérateur, et appelez donc les objets __ iter __ code>, qui dans La théorie peut avoir des effets secondaires, tandis que les appels HASATTR ci-dessus ne devraient pas avoir d'effets secondaires. Ok, alors ça appelle getattribute fort> qui aurait pu. Mais vous pouvez résoudre ce problème: p>

>>> def is_container_iterable(o):
...     try:
...         object.__getattribute__(o, '__iter__')
...     except AttributeError:
...         return False
...     try:
...         object.__getattribute__(o, '__next__')
...     except AttributeError:
...         return True
...     return False
... 
>>> is_container_iterable([])
True
>>> is_container_iterable(())
True
>>> is_container_iterable({})
True
>>> is_container_iterable(range(5))
True
>>> is_container_iterable(iter(range(5)))
False


2 commentaires

+1: Je ne m'attendais pas à ce qu'il y aurait un moyen de faire cela si la classe ne se dérange pas de la sous-classe de collections.itéable . (BTW, une classe peut-elle dériver de manière significative des deux itérant et itérateur ?)


Itérant dérive de itérateur, donc non.