Qual é a maneira canônica de verificar o tipo no Python?

1278

Qual é a melhor maneira de verificar se um determinado objeto é de um determinado tipo? Que tal verificar se o objeto herda de um determinado tipo?

Digamos que eu tenho um objeto o. Como verifico se é um str?

Herge
fonte
7
Bem, a abordagem canônica no Python é não verificar o tipo (a menos que você esteja depurando). Geralmente você apenas tenta usá-lo como uma string (por exemplo, concatenar com outras strings, imprimir no console, etc.); se você acha que pode falhar, use try / except ou hasattr. Dito isto, a resposta aceita é a maneira canônica de fazer o que você geralmente "não deveria fazer" no mundo Python. Para obter mais informações, pesquise no Google "digitação de pato Python" ou leia: voidspace.org.uk/python/articles/duck_typing.shtml stackoverflow.com/questions/610883/…
Jon Coombs
9
Eu acho que o Sr. Coombs está negligenciando exemplos como classes serializáveis ​​não JSON. Se você colocar uma grande parte dos dados através de uma função (cujo código não se pode influenciar), pode-se querer converter determinadas partes desses dados em, por exemplo, um <str> antes de passá-lo. Pelo menos é assim que eu acabei desta página ...
John Carrell
2
Parece que o motivo mais comum para pedir isso é que se deseja distinguir entre strings e iteráveis ​​de strings. Essa é uma pergunta complicada, porque strings são iteráveis ​​de strings - uma string de um caractere é até uma sequência de si mesma (da última vez que verifiquei - provavelmente não se deve confiar nela). Mas alguém teria alguma utilidade para algo parecido com barbante? Sim . Portanto, a resposta para "O que devo fazer para distinguir entre strings e outros iteráveis ​​de strings?" é corretamente: "Depende do que você está tentando fazer". :-D
clacke
2
As anotações do tipo Python agora são uma coisa. Dê uma olhada no mypy
Sheena

Respostas:

1522

Para verificar se oé uma instância strou qualquer subclasse de str, use isinstance (essa seria a maneira "canônica"):

if isinstance(o, str):

Para verificar se o tipo de oé exatamente str(excluir subclasses):

if type(o) is str:

O seguinte também funciona e pode ser útil em alguns casos:

if issubclass(type(o), str):

Consulte Funções internas na Referência da biblioteca Python para obter informações relevantes.

Mais uma observação: nesse caso, se você estiver usando o Python 2, poderá usar:

if isinstance(o, basestring):

porque isso também captura seqüências de caracteres Unicode ( unicodenão é uma subclasse de str; both stre unicodesão subclasses de basestring). Observe que basestringnão existe mais no Python 3, onde há uma separação estrita de strings ( str) e dados binários ( bytes).

Como alternativa, isinstanceaceita uma tupla de classes. Isso retornará Truese ofor uma instância de qualquer subclasse de qualquer um de (str, unicode):

if isinstance(o, (str, unicode)):
Fredrik Johansson
fonte
31
str .__ subclasses __ () retorna apenas as subclasses diretas de str e não faz o mesmo que issubclass () ou isinstance (). (Para fazer isso, você teria que chamar .__ subclasses de forma recursiva __ ().
Thomas Wouters
15
Essa é uma boa resposta, mas acho que realmente deveria começar com um aviso de que você normalmente não deveria fazer isso no Python. Como é, parece validar a suposição de que isso é uma "coisa canônica a ser feita em Python", o que não é.
Jon Coombs
4
Estas são as respostas python2. Por exemplo, não há base no python3.
dfrankow
4
Qual é a diferença entre instância e "exatamente"? Se type(a) is Objectentão não é verdade também isinstance(a, Object). No entanto, se type(a) is SubClassOfObject, então type(a) is Object == False, mas isinstance(a, Object) == True. Direita?
mavavilj
1
@mavavilj - a is bsignifica que aeb são exatamente a mesma coisa, ou seja, referências à mesma entidade na memória. Então, ae bteria que ser exatamente a mesma classe, não subclasses, como acontece com isinstance(). Veja por exemplo stackoverflow.com/a/133024/1072212
Terry Brown
196

A maneira mais pitônica de verificar o tipo de um objeto é ... não verificá-lo.

Como o Python incentiva o Duck Typing , você deve try...exceptusar os métodos do objeto da maneira que deseja. Portanto, se sua função estiver procurando por um objeto de arquivo gravável, não verifique se é uma subclasse de file, apenas tente usar seu .write()método!

É claro que, algumas vezes, essas abstrações agradáveis ​​quebram e isinstance(obj, cls)é o que você precisa. Mas use com moderação.

Dan Lenski
fonte
75
IMHO, a maneira mais pitônica é lidar com qualquer argumento que seja dado. No meu código, geralmente não consigo saber se recebo um objeto ou uma matriz de objetos e uso a verificação de tipo internamente para converter um único objeto em uma lista de um elemento.
sastanin
14
Em vez disso, apenas tentando usar seu método de gravação, há momentos em que você deseja fazer isso sem causar uma exceção. Neste caso, você poderia fazer ... if hasattr(ob, "write") and callable(ob.write): Ou poupar algum acesso dict ...func = getattr(ob, "write", None) if callable(func): ...
ideasman42
142
A digitação de pato é sobre o uso de uma biblioteca. A verificação de tipo é sobre escrever uma biblioteca. Não é o mesmo domínio do problema.
RickyA
16
@ RickyA, eu discordo. A digitação de pato é sobre a interação com objetos usando interfaces com semânticas conhecidas. Isso pode se aplicar ao código da biblioteca ou ao código que usa essa biblioteca.
precisa
6
@ nyuszika7h, No Python3, hasattrapenas suprime um AttributeError - Veja: docs.python.org/3.4/library/functions.html#hasattr
ideasman42
57

isinstance(o, str)retornará Truese oé um strou é de um tipo que herda str.

type(o) is strretornará Truese e somente se ofor um str. Ele retornará Falsese ofor de um tipo que herda str.

Herge
fonte
6
Obviamente, isso falhará se o objeto não for uma instância de 'str', mas sim algo parecido com uma string. Como unicode, mmap, UserString ou qualquer outro tipo definido pelo usuário. A abordagem usual no Python não é fazer checagens tipográficas.
Thomas Wouters
6
Você não precisa se desculpar, não há problema em responder sua própria pergunta. SO é para as respostas, não para o carma.
Eli Bendersky
2
Isso é muito útil. Porque a diferença entre isinstancee type(var) == type('')não é clara.
sastanin
30

Depois que a pergunta foi feita e respondida, dicas de tipo foram adicionadas ao Python . Dicas de tipo no Python permitem que os tipos sejam verificados, mas de uma maneira muito diferente das linguagens estaticamente tipadas. Dicas de tipo no Python associam os tipos esperados de argumentos a funções como dados acessíveis em tempo de execução associados a funções e isso permite que tipos sejam verificados. Exemplo de sintaxe do tipo dica:

def foo(i: int):
    return i

foo(5)
foo('oops')

Nesse caso, queremos que um erro seja acionado por foo('oops') já que o tipo anotada do argumento é int. A dica de tipo adicionado não causa um erro quando o script é executado normalmente. No entanto, ele adiciona atributos à função que descreve os tipos esperados que outros programas podem consultar e usar para verificar se há erros de tipo.

Um desses outros programas que podem ser usados ​​para encontrar o erro de tipo é mypy :

mypy script.py
script.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "int"

(Talvez você precise instalar a mypypartir do seu gerenciador de pacotes. Não acho que ele seja fornecido com o CPython, mas parece ter algum nível de "oficialidade".)

A verificação de tipo dessa maneira é diferente da verificação de tipo em idiomas compilados estaticamente. Como os tipos são dinâmicos no Python, a verificação de tipo deve ser feita em tempo de execução, o que impõe um custo - mesmo em programas corretos - se insistirmos que isso ocorra a todo momento. Verificações explícitas de tipo também podem ser mais restritivas do que o necessário e causar erros desnecessários (por exemplo, o argumento realmente precisa ser exatamentelist tipo ou alguma coisa iterável é suficiente?).

A vantagem da verificação explícita de tipo é que ela pode capturar erros mais cedo e fornecer mensagens de erro mais claras do que a digitação de pato. Os requisitos exatos de um tipo de pato só podem ser expressos com documentação externa (espero que seja completa e precisa) e erros de tipos incompatíveis possam ocorrer longe de onde eles se originam.

As dicas de tipo do Python são destinadas a oferecer um compromisso onde os tipos podem ser especificados e verificados, mas não há custo adicional durante a execução normal do código.

O typingpacote oferece variáveis ​​de tipo que podem ser usadas em dicas de tipo para expressar comportamentos necessários sem exigir tipos específicos. Por exemplo, inclui variáveis ​​como Iterablee Callablepara dicas para especificar a necessidade de qualquer tipo com esses comportamentos.

Embora as dicas de tipo sejam a maneira mais pitônica de verificar os tipos, muitas vezes é ainda mais pitônico não verificar os tipos e confiar na digitação do pato. As dicas de tipo são relativamente novas e o júri ainda não se manifestou quando é a solução mais pitônica. Uma comparação relativamente incontroversa, mas muito geral: as dicas de tipo fornecem uma forma de documentação que pode ser aplicada, permitem que o código gere erros mais cedo e mais fáceis de entender, podem detectar erros que a digitação de pato não pode e podem ser verificados estaticamente (de maneira incomum mas ainda está fora do tempo de execução). Por outro lado, a tipagem de patos é o caminho pitônico há muito tempo, não impõe a sobrecarga cognitiva da tipagem estática, é menos detalhada e aceita todos os tipos viáveis ​​e mais alguns.

Praxeolitic
fonte
2
-1: mypy se autodenomina especificamente um "verificador de tipo estático", portanto não tenho certeza de onde você obteve "a verificação de tipo deve ser feita em tempo de execução".
Kevin
@ Kevin Em retrospecto, essa foi uma digressão desnecessária, mas para aprofundar mais, as dicas de tipo do Python são transformadas em dados de tempo de execução e mypyé um módulo Python usado importlibpara acessar esses dados. Se isso é "verificação de tipo estático" é uma questão filosófica, mas é diferente do que a maioria esperaria, pois o intérprete de linguagem normal e o mecanismo de importação estão envolvidos.
Praxeolitic
4
Isso também não é verdade. Ele usa typed_ast, que por si só é apenas um clone do ast com recursos extras. o ast não importa módulos; ele os analisa em uma árvore de sintaxe abstrata.
Kevin
18

Aqui está um exemplo de como digitar patos é ruim sem saber quando é perigoso. Por exemplo: Aqui está o código Python (possivelmente omitindo o recuo adequado), observe que essa situação é evitável cuidando da instância e da subclasse de funções para garantir que, quando você realmente precisa de um pato, não receba uma bomba.

class Bomb:
    def __init__(self):
        ""

    def talk(self):
        self.explode()

    def explode(self):
        print "BOOM!, The bomb explodes."

class Duck:
    def __init__(self):
        ""
    def talk(self):
        print "I am a duck, I will not blow up if you ask me to talk."    

class Kid:
    kids_duck = None

    def __init__(self):
        print "Kid comes around a corner and asks you for money so he could buy a duck."

    def takeDuck(self, duck):
        self.kids_duck = duck
        print "The kid accepts the duck, and happily skips along"

    def doYourThing(self):
        print "The kid tries to get the duck to talk"
        self.kids_duck.talk()

myKid = Kid()
myBomb = Bomb()
myKid.takeDuck(myBomb)
myKid.doYourThing()
Dmitry
fonte
36
Mesmo com a verificação de tipo, você pode criar class EvilDuck(Duck)e substituir talk (). Ou, mais provavelmente, class ChineseCancerDuck(Duck)com um efeito colateral desagradável que só aparece anos depois. Você seria melhor fora apenas supervisionar o seu filho (e testar exaustivamente seus brinquedos :)
Brett Thomas
36
Bombas não falam. Não adicione métodos sem sentido e isso não acontecerá.
rightfold 18/03/14
7
@ Dmitry, essa é a crítica comum ao Duck Typing: en.wikipedia.org/wiki/Duck_typing#Criticism ... você está basicamente dizendo que qualquer interface para a qual a semântica não é imposta pelo idioma é ruim. Acredito que essa seja mais a abordagem do Java. O ponto principal da digitação de pato do Python é que ele só funciona quando há uma convenção comumente aceita sobre o significado de interfaces específicas. Por exemplo, você pode usar muitos códigos Python substituindo o __file__atributo (geralmente usado para identificar objetos semelhantes a arquivos) para significar outra coisa.
precisa
2
Tudo isso se resume à velha piada "Doutor, dói quando faço isso". ... "Então não faça isso." Insatisfatório para alguém que está acostumado a "se compila, roda", mas é por isso que testar a obsessão surgiu do mundo dinâmico da linguagem.
Clacke
1
@clacke basicamente, é muito caro impor tipos em tempo de execução estritamente porque TUDO deve ser um objeto (para mapear de uma string para qualquer tipo possível) e conveniente demais para não ter tipagem porque a tipagem permite técnicas de prototipagem realmente poderosas que superam coisas que normalmente são muito difíceis de fazer com interfaces rígidas. Além disso, qualquer linguagem estática enfrenta um ponto em que precisa criar tipagem de pato por meio de bibliotecas dinâmicas, avaliação e stringificação ou interfaces, e essas coisas não a tornam inerentemente má, apenas muito poderosa.
Dmitry
12
isinstance(o, str)

Link para documentos

Alexander Kojevnikov
fonte
1
Embora esse link possa responder à pergunta, é melhor incluir aqui as partes essenciais da resposta e fornecer o link para referência. As respostas somente para links podem se tornar inválidas se a página vinculada for alterada.
EKons
7

Eu acho que o legal de usar uma linguagem dinâmica como Python é que você realmente não deveria ter que verificar algo assim.

Eu chamaria os métodos necessários em seu objeto e pegaria um AttributeError. Posteriormente, isso permitirá que você chame seus métodos com outros objetos (aparentemente não relacionados) para realizar tarefas diferentes, como zombar de um objeto para teste.

Eu usei muito isso ao obter dados da Web com os urllib2.urlopen()quais retorna um arquivo como objeto. Por sua vez, isso pode ser passado para quase qualquer método que leia de um arquivo, porque implementa o mesmo read()método que um arquivo real.

Mas tenho certeza de que há um tempo e um local para usar isinstance(), caso contrário, provavelmente não estaria lá :)

Will Harding
fonte
Um bom exemplo de quando você deve usá-lo é se estiver analisando um objeto json dinâmico. Você não sabe com antecedência se um campo é uma string ou um dicionário.
Grey
6

Para validações de tipo mais complexas, eu gosto da abordagem de validação do typeguard com base nas anotações de dicas do tipo python:

from typeguard import check_type
from typing import List

try:
    check_type('mylist', [1, 2], List[int])
except TypeError as e:
    print(e)

Você pode executar validações muito complexas de maneira muito limpa e legível.

check_type('foo', [1, 3.14], List[Union[int, float]])
# vs
isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo) 
Granitosaurus
fonte
6

Você pode verificar o tipo de uma variável usando __name__ de um tipo.

Ex:

>>> a = [1,2,3,4]  
>>> b = 1  
>>> type(a).__name__
'list'
>>> type(a).__name__ == 'list'
True
>>> type(b).__name__ == 'list'
False
>>> type(b).__name__
'int'
Yerramsetty Rohit
fonte
Obrigado, este é o código secreto que eu queria quando o exibia como feedback para o usuário. Levei muito tempo para encontrar este ...
Aaron D. Marasco
5

Para Hugo:

Você provavelmente quer dizer, em listvez dearray , mas isso aponta para todo o problema com a verificação de tipo - você não quer saber se o objeto em questão é uma lista, deseja saber se é algum tipo de sequência ou se é um único objeto. Portanto, tente usá-lo como uma sequência.

Digamos que você queira adicionar o objeto a uma sequência existente ou, se for uma sequência de objetos, adicione todos eles

try:
   my_sequence.extend(o)
except TypeError:
  my_sequence.append(o)

Um truque é se você estiver trabalhando com strings e / ou sequências de strings - isso é complicado, já que uma string é frequentemente vista como um único objeto, mas também é uma sequência de caracteres. Pior que isso, já que é realmente uma sequência de strings de comprimento único.

Normalmente, escolho projetar minha API para que ela aceite apenas um único valor ou uma sequência - isso facilita as coisas. Não é difícil colocar um[ ] valor único quando você o transmite, se necessário.

(Embora isso possa causar erros nas seqüências de caracteres, como elas parecem seqüências).

Chris Barker
fonte
0

Uma maneira simples de verificar o tipo é compará-lo com algo cujo tipo você conhece.

>>> a  = 1
>>> type(a) == type(1)
True
>>> b = 'abc'
>>> type(b) == type('')
True
Ultrablendz
fonte
-1

Eu acho que a melhor maneira é digitar bem suas variáveis. Você pode fazer isso usando a biblioteca "digitando".

Exemplo:

from typing import NewType UserId = NewType ('UserId', int) some_id = UserId (524313) `

Consulte https://docs.python.org/3/library/typing.html

cherah30
fonte
-7

Você pode verificar com a linha abaixo para verificar qual tipo de caractere é o valor fornecido:

def chr_type(chrx):
    if chrx.isalpha()==True:
        return 'alpha'
    elif chrx.isdigit()==True:
        return 'numeric'
    else:
        return 'nothing'

chr_type("12)
Venkatesan
fonte
3
Tem certeza de que não deseja excluir esta resposta @Venkatesan?
Grey