Isto é o que eu normalmente faço para verificar se a entrada é um list
/ tuple
- mas não um str
. Porque muitas vezes me deparei com bugs em que uma função passa um str
objeto por engano, e a função alvo for x in lst
assume que lst
é realmente um list
ou tuple
.
assert isinstance(lst, (list, tuple))
Minha pergunta é: existe uma maneira melhor de conseguir isso?
Respostas:
Somente no python 2 (não no python 3):
Na verdade, é o que você deseja, caso contrário, você perderá muitas coisas que funcionam como listas, mas não são subclasses de
list
outuple
.fonte
basestring
se foi, e você apenas verificaisinstance(lst, str)
.set
, expressões de gerador, iteradores. Existem coisas exóticas, coisasmmap
menos exóticas,array
que agem como listas, e provavelmente muito mais que eu esqueci.lst
é iterável, enquanto o original fez (por exemplo, um int iria passar este cheque)assert isinstance(lst, (list, tuple)) and assert not isinstance(lst, basestring)
Lembre-se de que, em Python, queremos usar "digitação de pato". Portanto, qualquer coisa que atue como uma lista pode ser tratada como uma lista. Portanto, não verifique o tipo de uma lista, apenas veja se ela funciona como uma lista.
Mas as strings também funcionam como uma lista, e muitas vezes não é isso que queremos. Há momentos em que é até um problema! Portanto, verifique explicitamente se há uma string, mas use a digitação de pato.
Aqui está uma função que escrevi por diversão. É uma versão especial
repr()
que imprime qualquer sequência entre colchetes angulares ('<', '>').Isso é limpo e elegante, em geral. Mas o que esse
isinstance()
cheque está fazendo lá? Isso é meio que um hack. Mas é essencial.Essa função se chama recursivamente em qualquer coisa que atue como uma lista. Se não tratássemos a string especialmente, ela seria tratada como uma lista e dividiria um caractere de cada vez. Mas então a chamada recursiva tentaria tratar cada caractere como uma lista - e funcionaria! Mesmo uma cadeia de um caractere funciona como uma lista! A função continuaria se chamando recursivamente até o estouro da pilha.
Funções como esta, que dependem de cada chamada recursiva, dividindo o trabalho a ser realizado, têm cadeias de casos especiais - porque você não pode dividir uma cadeia abaixo do nível de uma cadeia de um caractere e até uma -caracter string atua como uma lista.
Nota: a
try
/except
é a maneira mais limpa de expressar nossas intenções. Mas se esse código for de alguma forma crítico em termos de tempo, podemos substituí-lo por algum tipo de teste para ver searg
é uma sequência. Em vez de testar o tipo, provavelmente devemos testar comportamentos. Se ele tem um.strip()
método, é uma string, então não considere uma sequência; caso contrário, se for indexável ou iterável, é uma sequência:EDIT: Eu escrevi originalmente o acima com uma verificação,
__getslice__()
mas notei que nacollections
documentação do módulo, o método interessante é__getitem__()
; isso faz sentido, é assim que você indexa um objeto. Isso parece mais fundamental do que__getslice__()
eu mudei o acima.fonte
srepr
é uma ideia muito interessante. Mas tenho uma opinião diferente da sua sobre se é necessário um caso especialstr
. Sim,str
é de longe a iterável mais óbvia e comum que causaria uma recursão infinita nosrepr
. Mas posso facilmente imaginar iteráveis definidos pelo usuário que se comportam da mesma maneira (com ou sem um bom motivo). Em vez de um caso especialstr
, devemos admitir que essa abordagem pode se deparar com uma recursão infinita e concordar com alguma maneira de lidar com ela. Vou postar minha sugestão em uma resposta.srepr()
está bem como está. Precisamos de uma função recursiva para lidar com coisas como uma lista aninhada dentro de outra lista; mas para strings, preferimos imprimi-las do"foo"
que como<'f', 'o', 'o'>
. Portanto, uma verificação explícita de uma sequência faz sentido aqui. Além disso, realmente não existem outros exemplos de tipos de dados nos quais a iteração sempre retorna uma iterável e a recursão sempre causa excesso de pilha; portanto, não precisamos de uma propriedade especial para testar isso ("A praticidade supera a pureza").__iter__()
método em Python 3, mas não em Python 2. Você está parênteses em faltais_sequence()
, deve ler-se:return (not hasattr(arg, "strip") and (hasattr(arg, "__getitem__") or hasattr(arg, "__iter__")))
fonte
if isinstance( H, (list, tuple) ): ...
é mais curta e mais clara.if type(H) in [list, tuple]:
Para Python 3:
Para Python 2:
fonte
collections.Sequence
mas eu testei e vejo que eles fazem. O mesmo acontecexrange
. Ainda melhor, este teste excluidict
, que possui ambos__getitem__
e__iter__
.inspect.getmro(list)
não incluiSequence
? Como devemos descobrir o que podemos fazerisinstance
quandogetmro
não mostra tudo?Sequence
é uma classe abstrata.Python com sabor PHP:
fonte
__getitem__
. Além disso, o nome é enganoso, pois também há módulo de matriz. E o var também pode ser um numpy.ndarray ou qualquer outro tipo que possua__getitem__
. Consulte stackoverflow.com/a/1835259/470560 para obter a resposta correta.str
também tem__getitem__
, portanto, o seu cheque não excluistr
__getitem__
aqui é um mau conselho.De um modo geral, o fato de uma função que itera sobre um objeto funcionar tanto em strings quanto em tuplas e listas é mais um recurso que um bug. Você certamente pode usar
isinstance
ou digitar para verificar um argumento, mas por que deveria?Isso soa como uma pergunta retórica, mas não é. A resposta para "por que devo verificar o tipo de argumento?" provavelmente vai sugerir uma solução para o problema real, não para o problema percebido. Por que é um erro quando uma string é passada para a função? Além disso: se é um bug quando uma string é passada para essa função, também é um bug se alguma outra iterável não listada / tupla for passada para ela? Por que ou por que não?
Acho que a resposta mais comum para a pergunta provavelmente é que os desenvolvedores que escrevem
f("abc")
esperam que a função se comporte como se tivessem escritof(["abc"])
. Provavelmente, há circunstâncias em que faz mais sentido proteger os desenvolvedores de si mesmos do que apoiar o caso de uso de iteração entre os caracteres em uma sequência. Mas eu pensaria muito nisso primeiro.fonte
Tente isso para facilitar a leitura e as práticas recomendadas:
Python2
Python3
Espero que ajude.
fonte
AttributeError: module 'types' has no attribute 'ListType'
from typing import List
->isinstance([1, 2, 3], List
=True
eisinstance("asd", List)
=False
O
str
objeto não tem um__iter__
atributopara que você possa fazer uma verificação
e isso também aumentará a importância
AssertionError
de qualquer outro objeto não iterável.Edit: Como Tim menciona nos comentários, isso funcionará apenas no python 2.x, não no 3.x
fonte
hasattr('','__iter__')
retornaTrue
. E, claro, isso faz sentido, pois você pode iterar sobre uma string.Isso não pretende responder diretamente ao OP, mas eu queria compartilhar algumas idéias relacionadas.
Eu estava muito interessado na resposta @steveha acima, que parecia dar um exemplo em que a digitação do pato parece quebrar. Pensando bem, no entanto, seu exemplo sugere que a digitação com patos é difícil de se adaptar, mas não sugere que
str
mereça um tratamento especial.Afinal, um não-
str
tipo (por exemplo, um tipo definido pelo usuário que mantém algumas estruturas recursivas complicadas) pode fazer com que asrepr
função @steveha cause uma recursão infinita. Embora isso seja bastante improvável, não podemos ignorar essa possibilidade. Portanto, ao invés de especial-invólucrostr
emsrepr
, devemos esclarecer o que queremossrepr
fazer quando um infinitas resultados recursão.Pode parecer que uma abordagem razoável seja simplesmente interromper a recursão no
srepr
momentolist(arg) == [arg]
. De fato, isso resolveria completamente o problema comstr
, sem nenhumisinstance
.No entanto, uma estrutura recursiva realmente complicada pode causar um loop infinito onde
list(arg) == [arg]
nunca acontece. Portanto, embora a verificação acima seja útil, não é suficiente. Precisamos de algo como um limite rígido na profundidade da recursão.Meu argumento é que, se você planeja lidar com tipos de argumentos arbitrários, o manuseio
str
por meio da digitação com patos é muito, muito mais fácil do que com os tipos mais gerais que você pode (teoricamente) encontrar. Portanto, se você sentir a necessidade de excluirstr
instâncias, deve exigir que o argumento seja uma instância de um dos poucos tipos especificados explicitamente.fonte
str
, que o código de caso especial trata. Mas talvez deva haver uma nova propriedade padrão que o código possa inspecionar,.__atomic__
digamos, que sinalize que algo não pode mais ser quebrado. Provavelmente é tarde demais para adicionar outra função integradaatomic()
ao Python, mas talvez possamos adicionarfrom collections import atomic
algo assim.Eu encontro uma função chamada is_sequence no tensorflow .
E verifiquei que atende às suas necessidades.
fonte
Eu faço isso em minhas caixas de teste.
Não testado em geradores, acho que você fica no próximo 'rendimento' se for passado em um gerador, o que pode estragar tudo a jusante. Mas, novamente, este é um 'unittest'
fonte
No modo "digitação de pato", que tal
ou
respectivamente. Isso evita o material
isinstance
/hasattr
introspecção.Você também pode verificar o contrário:
Todas as variantes, na verdade, não alteram o conteúdo da variável, mas implicam em uma reatribuição. Não tenho certeza se isso pode ser indesejável em algumas circunstâncias.
Curiosamente, com a atribuição "no local",
+=
nenhumTypeError
seria aumentado em qualquer caso selst
for uma lista (não uma tupla ). É por isso que a tarefa é feita dessa maneira. Talvez alguém possa esclarecer o porquê disso.fonte
maneira mais simples ... usando
any
eisinstance
fonte
Outra versão da digitação de pato para ajudar a distinguir objetos do tipo seqüência de caracteres de outros objetos do tipo seqüência.
A representação de string de objetos do tipo string é a própria string, portanto você pode verificar se obtém um objeto igual do
str
construtor:Isso deve funcionar para todos os objetos compatíveis com
str
e para todos os tipos de objetos iteráveis.fonte
O Python 3 tem isso:
Portanto, para verificar listas e tuplas, seria:
fonte
fonte
Apenas faça isso
fonte
em python> 3.6
fonte
str
, não uma string. Tenteisinstance('my_string', collections.abc.Container)
e você verá que ele retornaráTrue
. Isso ocorre porqueabc.Container
fornece o__contains__
método e as seqüências de caracteres, é claro.Costumo fazer isso (se realmente, realmente):
fonte
some_var
for uma instância de uma classe que é uma subclasse delist()
? Seu código não tem idéia do que fazer com ele, mesmo que funcione perfeitamente no código "faça algo com uma lista". E você raramente precisa se preocupar com a diferença entre uma lista e uma tupla. Desculpe -1.type(tuple())
-tuple
vai fazer. O mesmo para a lista. Além disso, bothstr
eunicode
extendbasestring
, que é o tipo de string real, então você deseja verificar isso.type(i) is list
. Além disso,type(list())
é apenaslist
ele mesmo ... Finalmente, isso não funciona normalmente com subclasses. Sei
é de fato e OrderedDict, ou algum tipo de nome nomeado, este código tratará como uma sequência.