C'est ce que je fais normalement pour vérifier que l'entrée est un list
/ tuple
- mais pas un str
. Parce que plusieurs fois, je suis tombé sur des bugs où une fonction passe un str
objet par erreur, et la fonction cible for x in lst
suppose que lst
c'est en fait un list
ou tuple
.
assert isinstance(lst, (list, tuple))
Ma question est: existe-t-il un meilleur moyen d'y parvenir?
Réponses:
En python 2 uniquement (pas en python 3):
C'est en fait ce que vous voulez, sinon vous passerez à côté de beaucoup de choses qui agissent comme des listes, mais ne sont pas des sous-classes de
list
outuple
.la source
basestring
c'est parti, et il suffit de vérifierisinstance(lst, str)
.set
, les expressions de générateur, les itérateurs. Il y a des choses exotiques commemmap
, des choses moins exotiquesarray
qui agissent à peu près comme des listes, et probablement beaucoup plus que j'ai oubliées.lst
est itérable, tandis que l'original l'a fait (par exemple, un int passerait cette vérification)assert isinstance(lst, (list, tuple)) and assert not isinstance(lst, basestring)
N'oubliez pas qu'en Python, nous voulons utiliser le "typage du canard". Ainsi, tout ce qui agit comme une liste peut être traité comme une liste. Donc, ne vérifiez pas le type d'une liste, voyez simplement si elle agit comme une liste.
Mais les chaînes agissent également comme une liste, et souvent ce n'est pas ce que nous voulons. Il y a des moments où c'est même un problème! Donc, recherchez explicitement une chaîne, mais utilisez ensuite la frappe de canard.
Voici une fonction que j'ai écrite pour le plaisir. Il s'agit d'une version spéciale
repr()
qui imprime toute séquence entre crochets ('<', '>').C'est propre et élégant, dans l'ensemble. Mais que fait ce
isinstance()
chèque là-bas? C'est une sorte de hack. Mais c'est essentiel.Cette fonction s'appelle récursivement sur tout ce qui agit comme une liste. Si nous ne gérions pas la chaîne spécialement, elle serait traitée comme une liste et divisée un caractère à la fois. Mais alors l'appel récursif essaierait de traiter chaque personnage comme une liste - et cela fonctionnerait! Même une chaîne à un caractère fonctionne comme une liste! La fonction continuerait à s'appeler récursivement jusqu'au débordement de la pile.
Les fonctions comme celle-ci, qui dépendent de chaque appel récursif décomposant le travail à effectuer, doivent avoir des chaînes de cas spécial - car vous ne pouvez pas décomposer une chaîne en dessous du niveau d'une chaîne à un caractère, et même une La chaîne de caractères agit comme une liste.
Remarque: le
try
/except
est le moyen le plus propre d'exprimer nos intentions. Mais si ce code était en quelque sorte critique dans le temps, nous pourrions vouloir le remplacer par une sorte de test pour voir s'ilarg
s'agit d'une séquence. Plutôt que de tester le type, nous devrions probablement tester les comportements. S'il a une.strip()
méthode, c'est une chaîne, alors ne la considérez pas comme une séquence; sinon, si elle est indexable ou itérable, c'est une séquence:EDIT: J'ai écrit à l'origine ce qui précède avec une vérification,
__getslice__()
mais j'ai remarqué que dans lacollections
documentation du module, la méthode intéressante est__getitem__()
; c'est logique, c'est ainsi que vous indexez un objet. Cela semble plus fondamental que__getslice__()
j'ai donc changé ce qui précède.la source
srepr
est une idée très intéressante. Mais j'ai une opinion différente de la vôtre sur la nécessité de cas particulierstr
. Oui,str
est de loin l'itérable le plus évident et le plus courant qui provoquerait une récursion infinie danssrepr
. Mais je peux facilement imaginer des itérables définis par l'utilisateur qui se comportent de la même manière (avec ou sans raison valable). Plutôt que de cas particulierstr
, nous devons admettre que cette approche peut se heurter à une récursion infinie et convenir d'une manière de la gérer. Je posterai ma suggestion dans une réponse.srepr()
va comme ça . Nous avons besoin d'une fonction récursive pour gérer des choses comme une liste imbriquée dans une autre liste; mais pour les chaînes, nous préférons les faire imprimer comme"foo"
plutôt que comme<'f', 'o', 'o'>
. Donc, une vérification explicite d'une chaîne a du sens ici. De plus, il n'y a vraiment pas d'autres exemples de types de données où l'itération retourne toujours un itérable et la récursivité provoquera toujours un débordement de pile, nous n'avons donc pas besoin d'une propriété spéciale pour tester cela ("La praticité bat la pureté").__iter__()
méthode en Python 3, mais pas en Python 2. Il vous manque des parenthèses dansis_sequence()
, il devrait lire:return (not hasattr(arg, "strip") and (hasattr(arg, "__getitem__") or hasattr(arg, "__iter__")))
la source
if isinstance( H, (list, tuple) ): ...
est plus courte et plus claire.if type(H) in [list, tuple]:
Pour Python 3:
Pour Python 2:
la source
collections.Sequence
mais je l'ai testé et je vois qu'ils le font. Il en va de mêmexrange
. Encore mieux, ce test exclutdict
, qui a à la fois__getitem__
et__iter__
.inspect.getmro(list)
ne comprend pasSequence
? Comment sommes-nous censés comprendre ce que nous pouvons faireisinstance
quandgetmro
ne montre pas tout?Sequence
est une classe abstraite.Python avec saveur PHP:
la source
__getitem__
. Le nom est également trompeur, car il existe également un module de tableau. Et le var pourrait également être un numpy.ndarray ou tout autre type, qui a__getitem__
. Voir stackoverflow.com/a/1835259/470560 pour la bonne réponse.str
aussi__getitem__
donc votre chèque n'exclut passtr
__getitem__
ici est un mauvais conseil.D'une manière générale, le fait qu'une fonction qui itère sur un objet fonctionne aussi bien sur les chaînes que sur les tuples et les listes est plus caractéristique que bug. Vous pouvez certainement utiliser
isinstance
ou taper du texte pour vérifier un argument, mais pourquoi devriez-vous?Cela ressemble à une question rhétorique, mais ce n'est pas le cas. La réponse à "pourquoi devrais-je vérifier le type de l'argument?" va probablement suggérer une solution au vrai problème, pas au problème perçu. Pourquoi est-ce un bug quand une chaîne est passée à la fonction? Aussi: si c'est un bug quand une chaîne est passée à cette fonction, est-ce aussi un bug si un autre itérable non list / tuple lui est passé? Pourquoi ou pourquoi pas?
Je pense que la réponse la plus courante à la question est probablement que les développeurs qui écrivent
f("abc")
s'attendent à ce que la fonction se comporte comme s'ils avaient écritf(["abc"])
. Il existe probablement des circonstances où il est plus logique de protéger les développeurs contre eux-mêmes que de prendre en charge le cas d'utilisation de l'itération sur les caractères d'une chaîne. Mais j'y réfléchirais longuement et longuement en premier.la source
Essayez ceci pour la lisibilité et les meilleures pratiques:
Python2
Python3
J'espère que cela aide.
la source
AttributeError: module 'types' has no attribute 'ListType'
from typing import List
->isinstance([1, 2, 3], List
=True
, etisinstance("asd", List)
=False
L'
str
objet n'a pas d'__iter__
attributafin que vous puissiez faire un contrôle
et cela soulèvera également une belle
AssertionError
pour tout autre objet non itérable.Edit: Comme Tim le mentionne dans les commentaires, cela ne fonctionnera qu'en python 2.x, pas 3.x
la source
hasattr('','__iter__')
revientTrue
. Et bien sûr, cela a du sens puisque vous pouvez parcourir une chaîne.Ce n'est pas destiné à répondre directement au PO, mais je voulais partager quelques idées connexes.
J'ai été très intéressé par la réponse de @steveha ci-dessus, qui semblait donner un exemple où la frappe de canard semble casser. À la réflexion, cependant, son exemple suggère que le typage du canard est difficile à respecter, mais il ne suggère pas qu'il
str
mérite une manipulation particulière.Après tout, un non-
str
type (par exemple, un type défini par l'utilisateur qui conserve certaines structures récursives compliquées) peut provoquer unesrepr
fonction @steveha provoquant une récursion infinie. Bien que cela soit certes assez improbable, nous ne pouvons pas ignorer cette possibilité. Par conséquent, plutôt que de cuvelage spécialstr
danssrepr
, nous devons clarifier ce que nous voulonssrepr
faire quand un résultat de récursion infinie.Il peut sembler qu'une approche raisonnable consiste simplement à briser la récursivité dans
srepr
l'instantlist(arg) == [arg]
. En fait, cela résoudrait complètement le problème avecstr
, sans aucunisinstance
.Cependant, une structure récursive vraiment compliquée peut provoquer une boucle infinie où il
list(arg) == [arg]
ne se produit jamais. Par conséquent, bien que la vérification ci-dessus soit utile, elle n'est pas suffisante. Nous avons besoin de quelque chose comme une limite stricte sur la profondeur de récursivité.Mon point est que si vous envisagez de gérer des types d'arguments arbitraires, la manipulation
str
via la frappe de canard est beaucoup, beaucoup plus facile que la gestion des types plus généraux que vous pouvez (théoriquement) rencontrer. Donc, si vous ressentez le besoin d'exclure desstr
instances, vous devez plutôt demander que l'argument soit une instance de l'un des rares types que vous spécifiez explicitement.la source
str
, que le code de cas spécial gère. Mais il devrait peut-être y avoir une nouvelle propriété standard que le code peut inspecter,.__atomic__
disons, qui signale que quelque chose ne peut plus être décomposé. Il est probablement trop tard pour ajouter une autre fonction intégréeatomic()
à Python, mais nous pouvons peut-être ajouterfrom collections import atomic
quelque chose.Je trouve une telle fonction nommée is_sequence dans tensorflow .
Et j'ai vérifié qu'il répond à vos besoins.
la source
Je le fais dans mes tests.
Non testé sur les générateurs, je pense que vous êtes laissé au prochain «rendement» s'il est passé dans un générateur, ce qui peut visser les choses en aval. Mais là encore, c'est un «unittest»
la source
De manière "typée canard", que diriez-
ou
respectivement. Cela évite le truc
isinstance
/hasattr
introspection.Vous pouvez également vérifier l'inverse:
Toutes les variantes ne modifient pas réellement le contenu de la variable, mais impliquent une réaffectation. Je ne sais pas si cela pourrait être indésirable dans certaines circonstances.
Fait intéressant, avec l'affectation "en place",
+=
aucunTypeError
ne serait soulevé dans tous les cas s'illst
s'agit d'une liste (et non d'un tuple ). C'est pourquoi la mission se fait de cette façon. Peut-être que quelqu'un peut nous expliquer pourquoi.la source
manière la plus simple ... en utilisant
any
etisinstance
la source
Une autre version du typage canard pour aider à distinguer les objets de type chaîne des autres objets de type séquence.
La représentation sous forme de chaîne des objets de type chaîne est la chaîne elle-même, vous pouvez donc vérifier si vous récupérez un objet égal du
str
constructeur:Cela devrait fonctionner pour tous les objets compatibles avec
str
et pour toutes sortes d'objets itérables.la source
Python 3 a ceci:
Donc, pour vérifier les listes et les tuples, ce serait:
la source
la source
Fais juste ça
la source
en python> 3,6
la source
str
, pas une chaîne. Essayezisinstance('my_string', collections.abc.Container)
et vous verrez qu'il reviendraTrue
. C'est parce queabc.Container
fournit la__contains__
méthode, et les chaînes l'ont, bien sûr.J'ai tendance à faire ça (si je devais vraiment, vraiment le faire):
la source
some_var
une instance d'une classe qui est une sous-classe delist()
? Votre code n'aura aucune idée de ce qu'il faut en faire, même s'il fonctionnera parfaitement sous le code "faire quelque chose avec une liste". Et vous avez rarement besoin de vous soucier de la différence entre une liste et un tuple. Désolé, -1.type(tuple())
-tuple
fera l'affaire. Idem pour la liste. Aussi, les deuxstr
etunicode
étendentbasestring
, qui est le vrai type de chaîne, vous devez donc vérifier cela à la place.type(i) is list
. En outre,type(list())
c'est justelist
lui - même ... Enfin, cela ne fonctionne pas correctement avec les sous-classes. Sii
est en fait et OrderedDict, ou une sorte de namedtuple, ce code traitera comme une chaîne.