Existe-t-il un moyen simple de déterminer si une variable est une liste, un dictionnaire ou autre chose? Je récupère un objet qui peut être l'un ou l'autre type et je dois être capable de faire la différence.
Pour obtenir le type d'un objet, vous pouvez utiliser la fonction type()
intégrée. Passer un objet comme seul paramètre retournera le type object de cet objet:
>>> type([]) is list
True
>>> type({}) is dict
True
>>> type('') is str
True
>>> type(0) is int
True
>>> type({})
<type 'dict'>
>>> type([])
<type 'list'>
Cela fonctionne bien sûr également pour les types personnalisés:
>>> class Test1 (object):
pass
>>> class Test2 (Test1):
pass
>>> a = Test1()
>>> b = Test2()
>>> type(a) is Test1
True
>>> type(b) is Test2
True
Notez que type()
renverra uniquement le type immédiat de l’objet, mais ne pourra pas vous informer de l’héritage de type.
>>> type(b) is Test1
False
Pour couvrir cela, vous devez utiliser la fonction isinstance
. Cela fonctionne bien sûr également pour les types intégrés:
>>> isinstance(b, Test1)
True
>>> isinstance(b, Test2)
True
>>> isinstance(a, Test1)
True
>>> isinstance(a, Test2)
False
>>> isinstance([], list)
True
>>> isinstance({}, dict)
True
isinstance()
est généralement le moyen préféré pour assurer le type d'un objet car il acceptera également les types dérivés. Donc, sauf si vous avez réellement besoin de l'objet type (pour quelque raison que ce soit), utiliser isinstance()
est préférable à type()
.
Le deuxième paramètre de isinstance()
accepte également un tuple de types, ce qui permet de vérifier plusieurs types à la fois. isinstance
retournera alors vrai si l'objet est de l'un de ces types:
>>> isinstance([], (Tuple, list, set))
True
Vous pouvez le faire en utilisant type()
:
>>> a = []
>>> type(a)
<type 'list'>
>>> f = ()
>>> type(f)
<type 'Tuple'>
Il serait peut-être préférable d'utiliser un bloc try
...except
. De cette façon, si vous avez une classe qui a des charlatans comme une liste, ou des charlatans comme un dict, elle se comportera correctement quel que soit son type réellement .
Pour clarifier, la méthode préférée pour "faire la différence" entre les types de variable consiste à utiliser quelque chose appelé typage de canard : tant que les méthodes (et types de retour) auxquelles une variable répond correspondent à ce que votre sous-programme attend, Traitez-le comme vous le souhaitez. Par exemple, si vous avez une classe qui surcharge les opérateurs de crochets avec getattr
et setattr
, mais utilise un schéma interne amusant, il serait approprié qu'elle se comporte comme un dictionnaire si c'est ce que vous essayez de faire. imiter.
L'autre problème avec la vérification type(A) is type(B)
est que si A
est une sous-classe de B
, il est évalué à false
lorsque, par programme, vous espérez que ce sera true
. Si un objet est une sous-classe d'une liste, il devrait fonctionner comme une liste: vérifier le type tel qu'il est présenté dans l'autre réponse évitera cela. (isinstance
fonctionnera, cependant).
Sur les instances d'objet, vous avez également le:
__class__
attribut. Voici un exemple tiré de la console Python 3.3
>>> str = "str"
>>> str.__class__
<class 'str'>
>>> i = 2
>>> i.__class__
<class 'int'>
>>> class Test():
... pass
...
>>> a = Test()
>>> a.__class__
<class '__main__.Test'>
Notez que dans python 3.x et dans les classes New-Style (disponibles à partir de Python 2.6), la classe et le type ont été fusionnés, ce qui peut parfois entraîner des résultats inattendus. Principalement pour cette raison, ma méthode préférée de test des types/classes est la fonction intégrée isinstance .
Déterminez le type d'un objet avec type
>>> obj = object()
>>> type(obj)
<class 'object'>
Bien que cela fonctionne, évitez les attributs de double soulignement, tels que __class__
- ils ne sont pas sémantiquement publics et, même s’ils ne le sont peut-être pas, les fonctions intégrées ont généralement un meilleur comportement.
>>> obj.__class__ # avoid this!
<class 'object'>
Existe-t-il un moyen simple de déterminer si une variable est une liste, un dictionnaire ou autre chose? Je récupère un objet qui peut être l'un ou l'autre type et je dois être capable de faire la différence.
Eh bien, c'est une question différente, n'utilisez pas le type - utilisez isinstance
:
def foo(obj):
"""given a string with items separated by spaces,
or a list or Tuple,
do something sensible
"""
if isinstance(obj, str):
obj = str.split()
return _foo_handles_only_lists_or_tuples(obj)
Ceci couvre le cas où votre utilisateur pourrait faire quelque chose d'intelligent ou de raisonnable en sous-classant str
- conformément au principe de Liskov Substitution, vous souhaitez pouvoir utiliser des instances de sous-classes sans rompre votre code - et isinstance
soutient cela.
Mieux encore, vous pouvez rechercher une classe de base abstraite spécifique dans collections
ou numbers
:
from collections import Iterable
from numbers import Number
def bar(obj):
"""does something sensible with an iterable of numbers,
or just one number
"""
if isinstance(obj, Number): # make it a 1-Tuple
obj = (obj,)
if not isinstance(obj, Iterable):
raise TypeError('obj must be either a number or iterable of numbers')
return _bar_sensible_with_iterable(obj)
Ou, peut-être mieux, utilisez-vous du typage, et ne tapez pas explicitement votre code. Le typage de canard prend en charge la substitution de Liskov avec plus d'élégance et moins de verbosité.
def baz(obj):
"""given an obj, a dict (or anything with an .items method)
do something sensible with each key-value pair
"""
for key, value in obj.items():
_baz_something_sensible(key, value)
type
pour obtenir la classe d'une instance.isinstance
pour vérifier explicitement les sous-classes réelles ou les abstractions enregistrées.Vous pouvez utiliser type()
ou isinstance()
.
>>> type([]) is list
True
Soyez averti que vous pouvez imbiber list
ou tout autre type en affectant une variable dans l'étendue actuelle du même nom.
>>> the_d = {}
>>> t = lambda x: "aight" if type(x) is dict else "NOPE"
>>> t(the_d) 'aight'
>>> dict = "dude."
>>> t(the_d) 'NOPE'
Ci-dessus, nous voyons que dict
est réaffecté à une chaîne, donc le test:
type({}) is dict
...échoue.
Pour contourner cela et utiliser type()
avec plus de prudence:
>>> import __builtin__
>>> the_d = {}
>>> type({}) is dict
True
>>> dict =""
>>> type({}) is dict
False
>>> type({}) is __builtin__.dict
True
soyez prudent en utilisant isinstance
isinstance(True, bool)
True
>>> isinstance(True, int)
True
mais tapez
type(True) == bool
True
>>> type(True) == int
False
Bien que les questions soient assez anciennes, j’ai trébuché là-dessus tout en trouvant moi-même un bon moyen, et j’estime qu’il reste à clarifier, au moins pour Python 2.x (n’a pas vérifiez sur Python 3, mais comme le problème se pose avec les classes classiques qui ont disparu avec cette version, peu importe.
J'essaie ici de répondre à la question du titre: comment puis-je déterminer le type d'un objet arbitraire? D'autres suggestions sur l'utilisation ou non d'isinstance sont acceptables dans de nombreux commentaires et réponses, mais je ne réponds pas à ces préoccupations.
Le problème principal avec l'approche type()
est que cela ne fonctionne pas correctement avec les instances de style ancien:
class One:
pass
class Two:
pass
o = One()
t = Two()
o_type = type(o)
t_type = type(t)
print "Are o and t instances of the same class?", o_type is t_type
L'exécution de cet extrait donnerait:
Are o and t instances of the same class? True
Ce qui, selon moi, n’est pas ce à quoi la plupart des gens s’attendraient.
L’approche __class__
est la plus proche de la correction, mais elle ne fonctionnera pas dans un cas crucial: lorsque l’objet transmis est une classe de style ancien (pas une instance!), car ces objets n'ont pas cet attribut.
C'est le plus petit fragment de code que je puisse imaginer qui réponde de manière cohérente à cette question légitime:
#!/usr/bin/env python
from types import ClassType
#we adopt the null object pattern in the (unlikely) case
#that __class__ is None for some strange reason
_NO_CLASS=object()
def get_object_type(obj):
obj_type = getattr(obj, "__class__", _NO_CLASS)
if obj_type is not _NO_CLASS:
return obj_type
# AFAIK the only situation where this happens is an old-style class
obj_type = type(obj)
if obj_type is not ClassType:
raise ValueError("Could not determine object '{}' type.".format(obj_type))
return obj_type
Outre les réponses précédentes, il convient de mentionner l'existence de collections.abc
qui contient plusieurs classes de base abstraites (ABC) qui complètent le typage.
Par exemple, au lieu de vérifier explicitement si quelque chose est une liste avec:
_isinstance(my_obj, list)
_
vous pouvez, si vous voulez seulement voir si l'objet que vous possédez permet d'obtenir des objets, utilisez collections.abc.Sequence
:
_from collections.abc import Sequence
isinstance(my_obj, Sequence)
_
si vous êtes strictement intéressé par les objets qui permettent d’obtenir, définissez et supprimer des éléments (c.-à-d. mutables ), vous opteriez pour _collections.abc.MutableSequence
_.
Beaucoup d'autres ABC y sont définis, Mapping
pour les objets pouvant être utilisés comme cartes, Iterable
, Callable
, etc. Une liste complète de tous ces éléments est disponible dans la documentation de _collections.abc
_.
En général, vous pouvez extraire une chaîne d'objet avec le nom de la classe,
str_class = object.__class__.__name__
et l'utiliser pour la comparaison,
if str_class == 'dict':
# blablabla..
Elif str_class == 'customclass':
# blebleble..
type()
est une meilleure solution que isinstance()
, en particulier pour booleans
:
True
et False
ne sont que des mots clés qui signifient 1
et 0
en python. Ainsi,
isinstance(True, int)
et
isinstance(False, int)
les deux retournent True
. Les deux booléens sont une instance d'un entier. type()
, cependant, est plus intelligent:
type(True) == int
renvoie False
.
Dans de nombreux cas pratiques, au lieu d'utiliser type
ou isinstance
, vous pouvez également utiliser @functools.singledispatch
, qui est utilisé pour définir fonctions génériques ( fonction composée de plusieurs fonctions implémentant la même opération pour différents types ).
En d'autres termes, vous voudriez l'utiliser quand vous avez un code comme celui-ci:
_def do_something(arg):
if isinstance(arg, int):
... # some code specific to processing integers
if isinstance(arg, str):
... # some code specific to processing strings
if isinstance(arg, list):
... # some code specific to processing lists
... # etc
_
Voici un petit exemple de son fonctionnement:
_from functools import singledispatch
@singledispatch
def say_type(arg):
raise NotImplementedError(f"I don't work with {type(arg)}")
@say_type.register
def _(arg: int):
print(f"{arg} is an integer")
@say_type.register
def _(arg: bool):
print(f"{arg} is a boolean")
_
_>>> say_type(0)
0 is an integer
>>> say_type(False)
False is a boolean
>>> say_type(dict())
# long error traceback ending with:
NotImplementedError: I don't work with <class 'dict'>
_
De plus, nous pouvons utiliser classes abstraites pour couvrir plusieurs types à la fois:
_from collections.abc import Sequence
@say_type.register
def _(arg: Sequence):
print(f"{arg} is a sequence!")
_
_>>> say_type([0, 1, 2])
[0, 1, 2] is a sequence!
>>> say_type((1, 2, 3))
(1, 2, 3) is a sequence!
_