Determinar o tipo de um objeto?

1791

Existe uma maneira simples de determinar se uma variável é uma lista, dicionário ou outra coisa? Estou recebendo um objeto de volta que pode ser do tipo e preciso saber a diferença.

Justin Ethier
fonte
44
Embora eu concorde com você em geral, há situações em que é útil saber. Nesse caso em particular, eu estava fazendo alguns hackers rápidos que acabei revertendo, então você está correto neste momento. Mas, em alguns casos - ao usar a reflexão, por exemplo - é importante saber com que tipo de objeto você está lidando.
Justin Ethier
67
@ S.Lott eu discordo disso; ao saber o tipo, você pode lidar com algumas entradas bastante variantes e ainda fazer a coisa certa. Ele permite solucionar problemas de interface inerentes à dependência da digitação pura de pato (por exemplo, o método .bark () em uma Árvore significa algo completamente diferente do que em um Cão.) Por exemplo, você pode criar uma função que funcione de alguma maneira um arquivo que aceita uma sequência (por exemplo, um caminho), um objeto de caminho ou uma lista. Todos têm interfaces diferentes, mas o resultado final é o mesmo: faça alguma operação nesse arquivo.
Robert P
22
@ S.Lott Eu esperava que fosse óbvio que é um exemplo artificial; no entanto, é um dos principais pontos fracassos da digitação de patos, e um que trynão ajuda. Por exemplo, se você sabia que um usuário poderia passar uma string ou uma matriz, ambos podem ser indexados, mas esse índice significa algo completamente diferente. Simplesmente contar com um try-catch nesses casos falhará de maneiras inesperadas e estranhas. Uma solução é criar um método separado, outra para adicionar uma pequena verificação de tipo. Eu pessoalmente prefiro o comportamento polimórfico ao longo de vários métodos que fazer quase a mesma coisa ... mas isso é apenas me :)
Robert P
22
@ S.Lott, e os testes de unidade? Às vezes, você deseja que seus testes verifiquem se uma função está retornando algo do tipo certo. Um exemplo muito real é quando você tem classe de fábrica.
Elliot Cameron #
17
Para um exemplo menos artificial, considere um serializador / desserializador. Por definição, você está convertendo entre objetos fornecidos pelo usuário e uma representação serializada. O serializador precisa determinar o tipo de objeto que você transmitiu e talvez você não tenha informações adequadas para determinar o tipo desserializado sem solicitar o tempo de execução (ou, no mínimo, pode ser necessário que a verificação de sanidade apareça dados incorretos antes de entrar seu sistema!)
Karl

Respostas:

1977

Existem duas funções internas que ajudam a identificar o tipo de um objeto. Você pode usar type() se precisar o tipo exato de um objeto, e isinstance()para verificar o tipo de um objeto contra alguma coisa. Geralmente, você deseja usar na isistance()maioria das vezes, uma vez que é muito robusto e também suporta herança de tipo.


Para obter o tipo real de um objeto, use a type()função interna. Passar um objeto como o único parâmetro retornará o objeto de tipo desse objeto:

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

Obviamente, isso também 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

Observe que type()retornará apenas o tipo imediato do objeto, mas não poderá informar sobre a herança do tipo.

>>> type(b) is Test1
False

Para cobrir isso, você deve usar a isinstancefunção Obviamente, isso também funciona para tipos internos:

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

isinstance()geralmente é a maneira preferida de garantir o tipo de um objeto, porque ele também aceita tipos derivados. Portanto, a menos que você realmente precise do objeto de texto (por qualquer motivo), o uso isinstance()é preferível type().

O segundo parâmetro de isinstance()também aceita uma tupla de tipos, portanto, é possível verificar vários tipos de uma só vez. isinstanceretornará true, se o objeto for de um desses tipos:

>>> isinstance([], (tuple, list, set))
True
cutucar
fonte
68
Eu acho que é mais claro para usar isem vez de ==como os tipos são singletons
John La Rooy
18
@gnibbler, nos casos você estaria typechecking (que você não deveria estar fazendo para começar), isinstanceé a forma preferida de qualquer forma, então, nem ==ou isprecisa ser usado.
Mike Graham
23
@ Mike Graham, há momentos em que typeé a melhor resposta. Há momentos em que isinstanceé a melhor resposta e há momentos em que a digitação de pato é a melhor resposta. É importante conhecer todas as opções para poder escolher qual é a mais apropriada para a situação.
John La Rooy
6
@ Sigibbler, isso pode ser, embora eu ainda não tenha corrido para a situação em type(foo) is SomeTypeque seria melhor do que isinstance(foo, SomeType).
Mike Graham
5
@poke: eu concordo totalmente com o PEP8, mas você está atacando um homem de palha aqui: a parte importante do argumento de Sven não era o PEP8, mas que você pode usar isinstancecomo base de dados (verificando uma variedade de tipos) e com limpe também uma sintaxe, que possui a grande vantagem de capturar subclasses. alguém usando OrderedDictodiaria que seu código falhasse porque ele aceita dictos puros.
ovinos voadores
166

Você pode fazer isso usando type():

>>> a = []
>>> type(a)
<type 'list'>
>>> f = ()
>>> type(f)
<type 'tuple'>
inkedmn
fonte
40

Pode ser mais Pythonic usar um bloco try... exceptDessa forma, se você tem uma classe que grasna como uma lista, ou grasna como um dicionário, ele irá se comportar corretamente, independentemente do que seu tipo realmente é.

Para esclarecer, o método preferido de "dizer a diferença" entre os tipos de variáveis ​​é com algo chamado tipagem de pato : desde que os métodos (e tipos de retorno) aos quais uma variável responde sejam o que sua sub-rotina espera, trate-a como o esperado. ser estar. Por exemplo, se você tem uma classe que sobrecarrega os operadores de colchete com getattre setattr, mas usa algum esquema interno engraçado, seria apropriado que ele se comporte como um dicionário, se é isso que ele está tentando imitar.

O outro problema com a type(A) is type(B)verificação é que, se Afor uma subclasse de B, ela avalia falsequando, programaticamente, você esperaria que fosse true. Se um objeto é uma subclasse de uma lista, ele deve funcionar como uma lista: a verificação do tipo apresentado na outra resposta impedirá isso. ( isinstancefuncionará, no entanto).

Seth Johnson
fonte
16
Digitar patos não é realmente dizer a diferença, no entanto. É sobre o uso de uma interface comum.
23811 Justin Ethier
5
Tenha cuidado - a maioria dos guias de estilo de codificação recomenda não usar o tratamento de exceções como parte do fluxo normal de código de controle, geralmente porque dificulta a leitura do código. try... excepté uma boa solução quando você deseja lidar com erros, mas não quando decide um comportamento com base no tipo.
Rens van der Heijden 31/03
34

Nas instâncias do objeto, você também tem o:

__class__

atributo. Aqui está um exemplo retirado do console do 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'>

Observe que no python 3.xe nas classes New-Style (disponíveis opcionalmente no Python 2.6) a classe e o tipo foram mesclados e isso pode levar a resultados inesperados. Principalmente por esse motivo, minha maneira favorita de testar tipos / classes é a função integrada da instância .

Lorenzo Persichetti
fonte
2
Seu ponto no final é muito importante. type (obj) é Class não estava funcionando corretamente, mas isinstance fez o truque. Entendo que essa instância é preferida de qualquer maneira, mas é mais benéfico do que apenas verificar os tipos derivados, conforme sugerido na resposta aceita.
Mstbaum
__class__está bem no Python 2.x, os únicos objetos no Python que não têm __class__atributo são as classes de estilo antigo AFAIK. A propósito, não entendo sua preocupação com o Python 3 - nessa versão, apenas todo objeto tem um __class__atributo que aponta para a classe apropriada.
27416 Alan Alzoni
21

Determinar o tipo de um objeto Python

Determine o tipo de um objeto com type

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

Embora funcione, evite atributos de sublinhado duplo como __class__- eles não são semanticamente públicos e, embora talvez não seja o caso, as funções internas geralmente têm um comportamento melhor.

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

verificação de tipo

Existe uma maneira simples de determinar se uma variável é uma lista, dicionário ou outra coisa? Estou recebendo um objeto de volta que pode ser do tipo e preciso saber a diferença.

Bem, essa é uma pergunta diferente, não 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)

Isso cobre o caso em que o usuário pode estar fazendo algo inteligente ou sensível ao subclassificar str- de acordo com o princípio da Substituição de Liskov, você deseja usar instâncias de subclasse sem quebrar seu código - e isinstancesuporta isso.

Usar abstrações

Ainda melhor, você pode procurar uma Classe Base Abstrata específica de collectionsou 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 simplesmente não verifique explicitamente o tipo

Ou, talvez o melhor de tudo, use digitação de pato e não verifique explicitamente seu código. A digitação de pato suporta a substituição de Liskov com mais elegância e menos verbosidade.

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)

Conclusão

  • Use typepara realmente obter a classe de uma instância.
  • Use isinstancepara verificar explicitamente subclasses reais ou abstrações registradas.
  • E evite a verificação de tipo onde faz sentido.
Aaron Hall
fonte
Sempre há try/ em exceptvez de verificar explicitamente.
toonarmycaptain
Presumivelmente, é isso que o usuário fará se não tiver certeza dos tipos pelos quais passará. Não gosto de desorganizar uma implementação correta com o tratamento de exceções, a menos que eu tenha algo muito bom a fazer com a exceção. A exceção levantada deve ser suficiente para informar ao usuário que ele precisa corrigir seu uso.
Aaron Hall
13

Você pode usar type()ou isinstance().

>>> type([]) is list
True

Esteja avisado de que você pode refogar listou qualquer outro tipo atribuindo uma variável no escopo atual do mesmo nome.

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

Acima, vemos que dicté reatribuído a uma string, portanto, o teste:

type({}) is dict

... falha.

Para contornar isso e usar com type()mais cautela:

>>> import __builtin__
>>> the_d = {}
>>> type({}) is dict
True
>>> dict =""
>>> type({}) is dict
False
>>> type({}) is __builtin__.dict
True
deed02392
fonte
2
Não sei se é necessário salientar que sombrear o nome de um tipo de dados interno é ruim para este caso. Sua dictstring também falhará em muitos outros códigos, como dict([("key1", "value1"), ("key2", "value2")]). A resposta para esse tipo de problema é "Então não faça isso" . Não sombreie os nomes dos tipos internos e espere que as coisas funcionem corretamente.
Blckknght
3
Eu concordo com você na parte "não faça isso". Mas, de fato, para dizer a alguém para não fazer algo, você deve pelo menos explicar por que não e achei que essa era uma oportunidade relevante para fazer exatamente isso. Eu pretendia que o método cauteloso parecesse feio e ilustrasse por que eles podem não querer fazê-lo, deixando-os decidir.
precisa saber é o seguinte
type () não funciona como esperado no Python 2.x para instâncias clássicas.
27416 Alan Alzoni
5

Embora as perguntas sejam bastante antigas, eu me deparei com isso enquanto descobria uma maneira adequada, e acho que ainda precisa ser esclarecida, pelo menos para o Python 2.x (não checou o Python 3, mas como o problema surge com as classes clássicas que foram lançadas nessa versão, provavelmente não importa).

Aqui, estou tentando responder à pergunta do título: como posso determinar o tipo de um objeto arbitrário ? Outras sugestões sobre o uso ou não do isinstance são boas em muitos comentários e respostas, mas não estou abordando essas preocupações.

O principal problema da type()abordagem é que ela não funciona corretamente com instâncias de estilo antigo :

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

A execução desse snippet renderia:

Are o and t instances of the same class? True

O que, argumento, não é o que a maioria das pessoas esperaria.

A __class__abordagem é a mais próxima da correção, mas não funcionará em um caso crucial: quando o objeto passado é uma classe de estilo antigo (não uma instância!), Pois esses objetos não possuem esse atributo.

Este é o menor trecho de código que eu poderia pensar que satisfaz essa pergunta legítima de uma maneira 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
fonte
5

tenha cuidado usando isinstance

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

mas digite

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

Como um aparte das respostas anteriores, vale a pena mencionar a existência, collections.abcque contém várias classes básicas abstratas (ABCs) que complementam a digitação de patos.

Por exemplo, em vez de verificar explicitamente se algo é uma lista com:

isinstance(my_obj, list)

você poderia, se estiver interessado apenas em ver se o objeto que você possui permite obter itens, use collections.abc.Sequence:

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

se você estiver estritamente interessado em objetos que permitem obter, configurar e excluir itens (por exemplo, seqüências mutáveis ), você optaria collections.abc.MutableSequence.

Muitos outros ABCs são definidas lá, Mappingpara objetos que podem ser usados como mapas, Iterable, Callable, et cetera. Uma lista completa de tudo isso pode ser vista na documentação para collections.abc.

Dimitris Fasarakis Hilliard
fonte
1

Em geral, você pode extrair uma string do objeto com o nome da classe,

str_class = object.__class__.__name__

e usá-lo para comparação,

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

Em muitos casos práticos, em vez de usar typeou isinstancevocê também pode usar @functools.singledispatch, que é usado para definir funções genéricas ( função composta de várias funções implementando a mesma operação para tipos diferentes ).

Em outras palavras, você gostaria de usá-lo quando tiver um código como o seguinte:

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

Aqui está um pequeno exemplo de como 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'>

Adicionalmente, podemos usar classes abstratas para cobrir vários tipos de uma só 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
fonte
0

type()é uma solução melhor do que isinstance(), especialmente para booleans:

Truee Falsesão apenas palavras-chave que significam 1e 0em python. Portanto,

isinstance(True, int)

e

isinstance(False, int)

ambos retornam True. Ambos os booleanos são uma instância de um número inteiro. type(), no entanto, é mais inteligente:

type(True) == int

retorna False.

Alec Alameddine
fonte