100 votes

comment dire qu'une variable est itérable mais pas une chaîne

J'ai une fonction qui prend un argument qui peut être soit un élément simple, soit un élément double:

 def iterable(arg)
    if #arg is an iterable:
        print "yes"
    else:
        print "no"
 

pour que:

>>> iterable (("f", "f"))
Oui

>>> iterable (["f", "f"])
Oui

>>> iterable ("ff")
non

Le problème est que cette chaîne est techniquement itérative, je ne peux donc pas simplement attraper ValueError en essayant arg[1] . Je ne veux pas utiliser isinstance (), car ce n'est pas une bonne pratique (ou du moins on me dit).

53voto

scvalex Points 5626

Utilisez isinstance (je ne vois pas pourquoi c'est une mauvaise pratique)

 import types
if not isinstance(arg, types.StringTypes):
 

Notez l'utilisation de StringTypes. Cela garantit que nous n'oublions pas un type de chaîne obscure.

En revanche, cela fonctionne également pour les classes de chaînes dérivées.

 class MyString(str):
    pass

isinstance(MyString("  "), types.StringTypes) # true
 

En outre, vous voudrez peut-être jeter un coup d’œil à cette question précédente .

À votre santé.

16voto

Alex Martelli Points 330805

Depuis la version 2.6 de Python, avec l'introduction de classes de base abstraites, isinstance (utilisé sur Abc, pas de classes de béton) est maintenant considéré comme parfaitement acceptable. Plus précisément:

from abc import ABCMeta, abstractmethod

class NonStringIterable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    @classmethod
    def __subclasshook__(cls, C):
        if cls is NonStringIterable:
            if any("__iter__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

C'est une copie exacte (en changeant seulement le nom de la classe) de l' Iterable tel que défini dans l' _abcoll.py (un détail d'implémentation de collections.py)... la raison pour laquelle cela fonctionne comme vous le souhaitez, tout en collections.Iterable ne l'est pas, c'est que ce dernier va le mile supplémentaire pour s'assurer des chaînes itératif, en appelant Iterable.register(str) explicitement juste après cette class déclaration.

Bien sûr, il est facile de compléter __subclasshook__ en retournant False avant l' any appel à d'autres classes que vous souhaitez exclure expressément de votre définition.

En tout cas, après avoir importé ce nouveau module, en myiter, isinstance('ciao', myiter.NonStringIterable) sera False, et isinstance([1,2,3], myiter.NonStringIterable)sera True, comme vous le demande-et en Python 2.6 et plus tard, ceci est considéré comme la bonne façon de constituer de tels contrôles... définir une classe de base abstraite et vérifiez isinstance sur il.

4voto

Nigel Small Points 2089

Je me rends compte que c’est un vieux message, mais j’ai pensé que cela valait la peine d’ajouter mon approche pour la postérité sur Internet. La fonction ci-dessous semble fonctionner pour moi dans la plupart des circonstances avec Python 2 et 3:

 def is_collection(obj):
    """ Returns true for any iterable which is not a string or byte sequence.
    """
    try:
        if isinstance(obj, unicode):
            return False
    except NameError:
        pass
    if isinstance(obj, bytes):
        return False
    try:
        iter(obj)
    except TypeError:
        return False
    try:
        hasattr(None, obj)
    except TypeError:
        return True
    return False
 

Ceci vérifie la présence d'une chaîne non-itérable en utilisant (mal) le hasattr intégré, qui générera un TypeError lorsque son deuxième argument n'est pas une chaîne ou une chaîne unicode.

0voto

Otto Allmendinger Points 11853

Comme vous le soulignez correctement, une seule chaîne de caractères est une séquence de caractères.

Donc, la chose que vous voulez vraiment faire est de trouver ce genre de séquence de arg est par l'utilisation de isinstance ou de type(a)==str.

Si vous voulez réaliser une fonction qui prend un nombre variable de paramètres, vous devez faire comme ceci:

def function(*args):
    # args is a tuple
    for arg in args:
        do_something(arg)

fonction("ff") et la fonction("ff", "ff") fonctionne.

Je ne peux pas voir un scénario où une isiterable() la fonction comme la vôtre est nécessaire. Il n'est pas isinstance() qui est mauvais style, mais des situations où vous avez besoin d'utiliser isinstance().

0voto

Helge Points 335

Les chaînes n'ont pas d'attribut en __iter__ , contrairement aux autres objets itérables:

 >>> hasattr( ("f","f"), '__iter__')
True

>>> hasattr( ["f","f"], '__iter__')
True

>>> hasattr("ff", '__iter__')
False
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X