J'ai une fonction qui prend un argument qui peut être un élément simple ou un élément double:
def iterable(arg)
if #arg is an iterable:
print "yes"
else:
print "no"
pour que:
>>> itérable (("f", "f")) Oui >>> itérable (["f", "f"]) Oui >>> itérable ("ff") non
Le problème est que la chaîne est techniquement itérable, donc je ne peux pas simplement attraper ValueError en essayant arg[1]
. Je ne veux pas utiliser isinstance (), car ce n'est pas une bonne pratique (du moins c'est ce qu'on me dit).
isinstance
c'est la façon de le faire dans des langages typés dynamiquement. Une chose à ne pas utiliser tous les jours, mais OK dans des cas justifiés.Réponses:
Utilisez une instance (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 obscur.
En revanche, cela fonctionne également pour les classes de chaînes dérivées.
class MyString(str): pass isinstance(MyString(" "), types.StringTypes) # true
Aussi, vous voudrez peut-être jeter un œil à cette question précédente .
À votre santé.
NB: le comportement a changé dans Python 3 au fur
StringTypes
et à mesure etbasestring
ne sont plus définis. Selon vos besoins, vous pouvez les remplacerisinstance
parstr
, ou par un sous-ensemble de(str, bytes, unicode)
, par exemple pour les utilisateurs Cython. Comme @Theron Luhn l'a mentionné, vous pouvez également utilisersix
.la source
isinstance
pourrait donc être le seul moyen.do isinstance(arg, str)
. Pour une version rétrocompatible, pensez à utiliser pythonhosted.org/six/#six.string_typestypes.StringTypes
n'est pas disponible dans Python3. Quelle est la valeur de Python2?À partir de 2017, voici une solution portable qui fonctionne avec toutes les versions de Python:
#!/usr/bin/env python import collections import six def iterable(arg): return ( isinstance(arg, collections.Iterable) and not isinstance(arg, six.string_types) ) # non-string iterables assert iterable(("f", "f")) # tuple assert iterable(["f", "f"]) # list assert iterable(iter("ff")) # iterator assert iterable(range(44)) # generator assert iterable(b"ff") # bytes (Python 2 calls this a string) # strings or non-iterables assert not iterable(u"ff") # string assert not iterable(44) # integer assert not iterable(iterable) # function
la source
Depuis Python 2.6, avec l'introduction de classes de base abstraites,
isinstance
(utilisé sur ABC, pas sur des classes concrètes) 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
Ceci est une copie exacte (en changeant uniquement le nom de la classe) de
Iterable
tel que défini dans_abcoll.py
(un détail d'implémentation decollections.py
) ... la raison pour laquelle cela fonctionne comme vous le souhaitez, alors que cecollections.Iterable
n'est pas le cas, est que ce dernier fait un effort supplémentaire pour s'assurer que les chaînes sont considéré comme itérable, en appelantIterable.register(str)
explicitement juste après cetteclass
instruction.Bien sûr, il est facile d'augmenter
__subclasshook__
en retournantFalse
avant l'any
appel pour d'autres classes que vous souhaitez exclure spécifiquement de votre définition.Dans tous les cas, après avoir importé ce nouveau module tel que
myiter
,isinstance('ciao', myiter.NonStringIterable)
seraFalse
etisinstance([1,2,3], myiter.NonStringIterable)
seraTrue
, comme vous le demandez - et dans Python 2.6 et versions ultérieures, cela est considéré comme la bonne façon d'incarner de telles vérifications ... définissez une classe de base abstraite et vérifiez-isinstance
le.la source
isinstance('spam', NonStringIterable)
renvoieTrue
.__iter__
est maintenant implémenté dans des chaînes en Python 3. Donc mon paragraphe "facile à augmenter" devient applicable etif issublass(cls, str): return False
doit par exemple être ajouté au début de__subclasshook__
(ainsi que toute autre classe qui définit__iter__
mais dans votre l'état d'esprit ne doit pas être accepté comme des «itérables sans chaîne»).if issublass(C, str): return False
devrait être ajouté?Je me rends compte que c'est un ancien article, mais j'ai pensé qu'il 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 cas 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
Cela vérifie une non-chaîne itérable par (mis) en utilisant le intégré
hasattr
qui lèvera aTypeError
lorsque son deuxième argument n'est pas une chaîne ou une chaîne Unicode.la source
En combinant les réponses précédentes, j'utilise:
import types import collections #[...] if isinstance(var, types.StringTypes ) \ or not isinstance(var, collections.Iterable): #[Do stuff...]
Ce n'est pas une preuve à 100%, mais si un objet n'est pas un itérable, vous pouvez toujours le laisser passer et retomber dans la frappe de canard.
Edit: Python3
types.StringTypes == (str, unicode)
. L'équivalent Phython3 est:if isinstance(var, str ) \ or not isinstance(var, collections.Iterable):
la source
2.x
J'aurais suggéré:
hasattr(x, '__iter__')
ou au vu du commentaire de David Charles modifiant ceci pour Python3, qu'en est-il:
hasattr(x, '__iter__') and not isinstance(x, (str, bytes))
3.x
le
basestring
type abstrait intégré a été supprimé . Utilisezstr
plutôt. Les typesstr
etbytes
n'ont pas suffisamment de fonctionnalités en commun pour justifier une classe de base partagée.la source
__iter__
qu'il y a des chaînes en Python 3?Comme vous le faites remarquer correctement, une seule chaîne est une séquence de caractères.
Donc, la chose que vous voulez vraiment faire est de savoir quel type de séquence
arg
est en utilisant isinstance ou type (a) == str.Si vous voulez réaliser une fonction qui prend une quantité variable de paramètres, vous devriez le faire comme ceci:
def function(*args): # args is a tuple for arg in args: do_something(arg)
function ("ff") et function ("ff", "ff") fonctionneront.
Je ne vois pas de scénario où une fonction isiterable () comme la vôtre est nécessaire. Ce n'est pas isinstance () qui est de mauvais style mais des situations où vous devez utiliser isinstance ().
la source
type(a) == str
doit être évitée. C'est une mauvaise pratique car elle ne prend pas en compte des types similaires ou des types dérivés destr
.type
ne monte pas dans la hiérarchie des types, alors qu'il leisinstance
fait, il est donc préférable d'utiliserisinstance
.Pour développer explicitement l'excellent hack d'Alex Martelli
collections.py
et répondre à certaines des questions qui l'entourent: La solution de travail actuelle dans python 3.6+ estimport collections import _collections_abc as cabc import abc class NonStringIterable(metaclass=abc.ABCMeta): __slots__ = () @abc.abstractmethod def __iter__(self): while False: yield None @classmethod def __subclasshook__(cls, c): if cls is NonStringIterable: if issubclass(c, str): return False return cabc._check_methods(c, "__iter__") return NotImplemented
et démontré
>>> typs = ['string', iter(''), list(), dict(), tuple(), set()] >>> [isinstance(o, NonStringIterable) for o in typs] [False, True, True, True, True, True]
Si vous souhaitez ajouter
iter('')
dans les exclusions, par exemple, modifiez la ligneif issubclass(c, str): return False
être
# `str_iterator` is just a shortcut for `type(iter(''))`* if issubclass(c, (str, cabc.str_iterator)): return False
obtenir
[False, False, True, True, True, True]
la source