Determinar el tipo de un objeto?

1791

¿Hay una manera simple de determinar si una variable es una lista, un diccionario u otra cosa? Estoy recuperando un objeto que puede ser de cualquier tipo y necesito poder notar la diferencia.

Justin Ethier
fuente
44
Aunque en general estoy de acuerdo con usted, hay situaciones en las que es útil saberlo. En este caso particular, estaba haciendo un hackeo rápido que eventualmente revertí, así que esta vez tienes razón. Pero en algunos casos, cuando se usa la reflexión, por ejemplo, es importante saber con qué tipo de objeto se trata.
Justin Ethier
67
@ S.Lott no estaría de acuerdo con eso; al poder conocer el tipo, puede lidiar con una entrada bastante variante y seguir haciendo lo correcto. Le permite solucionar problemas de interfaz inherentes a depender de la mecanografía pura (por ejemplo, el método .bark () en un árbol significa algo completamente diferente que en un perro). Por ejemplo, podría hacer una función que funcione un poco en un archivo que acepta una cadena (por ejemplo, una ruta), un objeto de ruta o una lista. Todos tienen interfaces diferentes, pero el resultado final es el mismo: realice alguna operación en ese archivo.
Robert P
22
@ S.Lott Esperaba que fuera obvio que es un ejemplo artificial; Sin embargo, es un punto de falla importante en la escritura de patos, y uno que tryno ayuda. Por ejemplo, si sabía que un usuario podía pasar una cadena o una matriz, ambos son indexables, pero ese índice significa algo completamente diferente. Simplemente confiar en un try-catch en esos casos fallará de manera inesperada y extraña. Una solución es hacer un método separado, otra para agregar una pequeña comprobación de tipos. Yo personalmente prefiero comportamiento polimórfico a través de múltiples métodos que hacen casi lo mismo ... pero eso es sólo conmigo :)
Robert P
22
@ S.Lott, ¿qué pasa con las pruebas unitarias? A veces, desea que sus pruebas verifiquen que una función está devolviendo algo del tipo correcto. Un ejemplo muy real es cuando tienes clase de fábrica.
Elliot Cameron
17
Para un ejemplo menos artificial, considere un serializador / deserializador. Por definición, está convirtiendo entre objetos proporcionados por el usuario y una representación serializada. El serializador debe determinar el tipo de objeto que ingresó, y es posible que no tenga la información adecuada para determinar el tipo deserializado sin preguntar el tiempo de ejecución (o, al menos, puede necesitarlo para verificar la sanidad para capturar datos incorrectos antes de que entre su sistema!)
Karl

Respuestas:

1977

Hay dos funciones integradas que lo ayudan a identificar el tipo de un objeto. Puede usarlo type() si necesita el tipo exacto de un objeto, y isinstance()para verificar el tipo de un objeto contra algo. Por lo general, desea usar la isistance()mayoría de las veces ya que es muy robusto y también admite la herencia de tipos.


Para obtener el tipo real de un objeto, utiliza la type()función incorporada. Pasar un objeto como el único parámetro devolverá el tipo de objeto de ese objeto:

>>> type([]) is list
True
>>> type({}) is dict
True
>>> type('') is str
True
>>> type(0) is int
True

Por supuesto, esto también funciona para tipos personalizados:

>>> class Test1 (object):
        pass
>>> class Test2 (Test1):
        pass
>>> a = Test1()
>>> b = Test2()
>>> type(a) is Test1
True
>>> type(b) is Test2
True

Tenga en cuenta que type()solo devolverá el tipo inmediato del objeto, pero no podrá informarle sobre la herencia de tipos.

>>> type(b) is Test1
False

Para cubrir eso, debes usar la isinstancefunción. Por supuesto, esto también funciona para los tipos incorporados:

>>> isinstance(b, Test1)
True
>>> isinstance(b, Test2)
True
>>> isinstance(a, Test1)
True
>>> isinstance(a, Test2)
False
>>> isinstance([], list)
True
>>> isinstance({}, dict)
True

isinstance()suele ser la forma preferida de asegurar el tipo de un objeto porque también aceptará tipos derivados. Por lo tanto, a menos que realmente necesite el objeto type (por cualquier razón), isinstance()se prefiere usar type().

El segundo parámetro de isinstance()también acepta una tupla de tipos, por lo que es posible verificar múltiples tipos a la vez. isinstanceluego devolverá verdadero, si el objeto es de cualquiera de esos tipos:

>>> isinstance([], (tuple, list, set))
True
dar un toque
fuente
68
Creo que es más fácil de usar en islugar de ==como los tipos son singletons
John La Rooy
18
@gnibbler, en los casos que sería verificación de tipos (que no se debe hacer para empezar), isinstancees la forma preferida de todos modos, por lo que ninguno ==o isse necesita usar.
Mike Graham
23
@ Mike Graham, hay momentos en que typees la mejor respuesta. Hay momentos en que isinstancees la mejor respuesta y hay momentos en que escribir pato es la mejor respuesta. Es importante conocer todas las opciones para poder elegir cuál es más apropiado para la situación.
John La Rooy
66
@gnibbler, puede ser, aunque todavía no me he encontrado con la situación en la type(foo) is SomeTypeque sería mejor que isinstance(foo, SomeType).
Mike Graham
55
@poke: estoy totalmente de acuerdo con PEP8, pero estás atacando a un hombre de paja aquí: la parte importante del argumento de Sven no fue PEP8, sino que también puedes usarlo isinstancepara tu caso de uso (verificar una variedad de tipos), y con como una sintaxis limpia, que tiene la gran ventaja de que puede capturar subclases. alguien que use OrderedDictodiaría que su código fallara porque solo acepta dictados puros.
ovejas voladoras
166

Puedes hacerlo usando type():

>>> a = []
>>> type(a)
<type 'list'>
>>> f = ()
>>> type(f)
<type 'tuple'>
entintado
fuente
40

Podría ser más Pythonic usar un try... exceptbloque. De esa manera, si tiene una clase que grazna como una lista, o grazna como un dict, se comportará correctamente independientemente de cuál sea realmente su tipo .

Para aclarar, el método preferido para "decir la diferencia" entre los tipos de variables es con algo llamado pato escribiendo : siempre que los métodos (y los tipos de retorno) a los que responde una variable sean lo que su subrutina espera, trátelo como lo espera. ser - estar. Por ejemplo, si tiene una clase que sobrecarga los operadores de corchetes con getattry setattr, pero usa algún esquema interno divertido, sería apropiado que se comportara como un diccionario si eso es lo que está tratando de emular.

El otro problema con la type(A) is type(B)comprobación es que si Aes una subclase de B, se evalúa falsecuándo, programáticamente, esperaría que lo fuera true. Si un objeto es una subclase de una lista, debería funcionar como una lista: verificar el tipo como se presenta en la otra respuesta evitará esto. ( isinstancefuncionará, sin embargo).

Seth Johnson
fuente
16
Sin embargo, escribir en patos no se trata realmente de decir la diferencia. Se trata de usar una interfaz común.
Justin Ethier
55
Tenga cuidado: la mayoría de las guías de estilo de codificación recomiendan no utilizar el manejo de excepciones como parte del flujo de control normal del código, generalmente porque hace que el código sea difícil de leer. try... exceptes una buena solución cuando quiere lidiar con errores, pero no cuando decide un comportamiento basado en el tipo.
Rens van der Heijden
34

En instancias de objeto también tiene el:

__class__

atributo. Aquí hay una muestra tomada de la consola 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'>

Tenga en cuenta que en python 3.xy en las clases New-Style (disponibles opcionalmente desde Python 2.6), la clase y el tipo se han fusionado y esto a veces puede conducir a resultados inesperados. Principalmente por esta razón, mi forma favorita de probar tipos / clases es con la función integrada isinstance .

Lorenzo Persichetti
fuente
2
Tu punto al final es muy importante. type (obj) es Class no funcionaba correctamente, pero su instancia hizo el truco. Entiendo que de todos modos se prefiere esta instancia, pero es más beneficioso que simplemente verificar los tipos derivados, como se sugiere en la respuesta aceptada.
mstbaum
__class__en su mayoría está bien en Python 2.x, los únicos objetos en Python que no tienen __class__atributo son las clases de estilo antiguo AFAIK. Por cierto, no entiendo su preocupación de Python 3: en tal versión, solo cada objeto tiene un __class__atributo que apunta a la clase adecuada.
Alan Franzoni
21

Determinar el tipo de un objeto Python

Determine el tipo de un objeto con type

>>> obj = object()
>>> type(obj)
<class 'object'>

Aunque funciona, evite los atributos de subrayado doble como __class__: no son semánticamente públicos y, aunque tal vez no en este caso, las funciones integradas suelen tener un mejor comportamiento.

>>> obj.__class__ # avoid this!
<class 'object'>

verificación de tipo

¿Hay una manera simple de determinar si una variable es una lista, un diccionario u otra cosa? Estoy recuperando un objeto que puede ser de cualquier tipo y necesito poder notar la diferencia.

Bueno, esa es una pregunta diferente, no use type - use 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)

Esto cubre el caso en el que su usuario podría estar haciendo algo inteligente o sensato al subclasificar str, de acuerdo con el principio de la sustitución de Liskov, desea poder usar instancias de subclase sin romper su código, y lo isinstanceadmite.

Usa abstracciones

Aún mejor, puede buscar una clase base abstracta específica de collectionso 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)

O simplemente no explícitamente la verificación de tipo

O, quizás lo mejor de todo, utilice la escritura de pato y no verifique explícitamente su código. La escritura de patos admite la sustitución de Liskov con más elegancia y menos verbosidad.

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)

Conclusión

  • Úselo typepara obtener realmente la clase de una instancia.
  • Use isinstancepara verificar explícitamente las subclases reales o las abstracciones registradas.
  • Y simplemente evite la verificación de tipo donde tenga sentido.
Aaron Hall
fuente
Siempre hay try/ en exceptlugar de verificar explícitamente.
toonarmycaptain
Presumiblemente, eso es lo que hará el usuario si no está seguro de los tipos que transmitirá. No me gusta saturar una implementación correcta con manejo de excepciones a menos que tenga algo muy bueno que hacer con la excepción. La excepción planteada debería ser suficiente para informar al usuario que necesita corregir su uso.
Aaron Hall
13

Puedes usar type()o isinstance().

>>> type([]) is list
True

Tenga en cuenta que puede clobber listo cualquier otro tipo asignando una variable en el alcance actual del mismo nombre.

>>> the_d = {}
>>> t = lambda x: "aight" if type(x) is dict else "NOPE"
>>> t(the_d) 'aight'
>>> dict = "dude."
>>> t(the_d) 'NOPE'

Arriba vemos que dictse reasigna a una cadena, por lo tanto, la prueba:

type({}) is dict

... falla

Para evitar esto y usarlo con type()más precaución:

>>> import __builtin__
>>> the_d = {}
>>> type({}) is dict
True
>>> dict =""
>>> type({}) is dict
False
>>> type({}) is __builtin__.dict
True
escritura02392
fuente
2
No estoy seguro de que sea necesario señalar que sombrear el nombre de un tipo de datos incorporado es malo para este caso. Su dictcadena también fallará para muchos otros códigos, como dict([("key1", "value1"), ("key2", "value2")]). La respuesta para ese tipo de problemas es "Entonces no hagas eso" . No sombree los nombres de tipos incorporados y espere que las cosas funcionen correctamente.
Blckknght
3
Estoy de acuerdo contigo en la parte de "no hagas eso". Pero, de hecho, para decirle a alguien que no haga algo, al menos debería explicar por qué no y pensé que esta era una oportunidad relevante para hacer precisamente eso. Me refería al método cauteloso de parecer feo e ilustrar por qué no querrían hacerlo, dejándolos decidir.
deed02392
type () no funciona como se esperaba en Python 2.x para instancias clásicas.
Alan Franzoni
5

Si bien las preguntas son bastante antiguas, me topé con esto mientras descubría una forma adecuada por mí mismo, y creo que aún necesita aclaraciones, al menos para Python 2.x (no comprobé Python 3, pero dado que el problema surge con las clases clásicas) que se han ido en tal versión, probablemente no importa).

Aquí estoy tratando de responder la pregunta del título: ¿cómo puedo determinar el tipo de un objeto arbitrario ? Otras sugerencias sobre el uso o no uso de isinstance están bien en muchos comentarios y respuestas, pero no estoy abordando esas preocupaciones.

El problema principal con el type()enfoque es que no funciona correctamente con instancias de estilo antiguo :

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

Ejecutar este fragmento produciría:

Are o and t instances of the same class? True

Lo cual, sostengo, no es lo que la mayoría de la gente esperaría.

El __class__enfoque es el más cercano a la corrección, pero no funcionará en un caso crucial: cuando el objeto pasado es una clase de estilo antiguo (¡no una instancia!), Ya que esos objetos carecen de dicho atributo.

Este es el fragmento de código más pequeño que se me ocurre que satisface una pregunta tan legítima de manera consistente:

#!/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
Alan Franzoni
fuente
5

tenga cuidado al usar isinstance

isinstance(True, bool)
True
>>> isinstance(True, int)
True

pero escribe

type(True) == bool
True
>>> type(True) == int
False
tnusraddinov
fuente
3

Como un aparte de las respuestas anteriores, vale la pena mencionar que la existencia collections.abccontiene varias clases base abstractas (ABC) que complementan la tipificación de pato.

Por ejemplo, en lugar de verificar explícitamente si algo es una lista con:

isinstance(my_obj, list)

podría, si solo le interesa ver si el objeto que tiene permite obtener elementos, use collections.abc.Sequence:

from collections.abc import Sequence
isinstance(my_obj, Sequence) 

si está estrictamente interesado en los objetos que permiten obtener, configurar y eliminar elementos (es decir, secuencias mutables ), optaría por collections.abc.MutableSequence.

Muchas otras ABC se definen allí, Mappingpara los objetos que se pueden utilizar como mapas, Iterable, Callable, etcétera. Se puede ver una lista completa de todos estos en la documentación de collections.abc.

Dimitris Fasarakis Hilliard
fuente
1

En general, puede extraer una cadena de objeto con el nombre de la clase,

str_class = object.__class__.__name__

y usándolo para comparar

if str_class == 'dict':
    # blablabla..
elif str_class == 'customclass':
    # blebleble..
José Crespo Barrios
fuente
1

En muchos casos prácticos en lugar de usar typeo isinstancetambién puede usar @functools.singledispatch, que se usa para definir funciones genéricas ( función compuesta de múltiples funciones que implementan la misma operación para diferentes tipos ).

En otras palabras, querrá usarlo cuando tenga un código como el siguiente:

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

Aquí hay un pequeño ejemplo de cómo funciona:

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'>

Además, podemos usar clases abstractas para cubrir varios tipos a la vez:

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!
Georgy
fuente
0

type()es una mejor solución que isinstance(), particularmente para booleans:

Truey Falseson solo palabras clave que significan 1y 0en python. Así,

isinstance(True, int)

y

isinstance(False, int)

ambos regresan True. Ambos booleanos son una instancia de un entero. type(), sin embargo, es más inteligente:

type(True) == int

vuelve False.

Alec Alameddine
fuente