__getitem__Também é suficiente para fazer um objeto iterável
Kos
4
FWIW: será iter(myObj)bem - sucedido se isinstance(myObj, dict), portanto, se você estiver olhando para uma myObjque possa ser uma sequência de dicts ou uma única dict, terá êxito nos dois casos. Uma sutileza que é importante se você quiser saber o que é uma sequência e o que não é. (em Python 2)
Ben Mosher
7
__getitem__também é suficiente para tornar um objeto iterável ... se começar no índice zero .
Carlos A. Gómez
Respostas:
27
Ultimamente tenho estudado esse problema. Com base nisso, minha conclusão é que hoje em dia essa é a melhor abordagem:
from collections.abc importIterable# drop `.abc` with Python 2.7 or lowerdef iterable(obj):return isinstance(obj,Iterable)
O exposto acima já foi recomendado anteriormente, mas o consenso geral é de que o uso iter()seria melhor:
Também usamos iter()nosso código para esse fim, mas ultimamente comecei a ficar cada vez mais irritado com objetos que apenas __getitem__são considerados iteráveis. Há razões válidas para ter __getitem__em um objeto não iterável e, com elas, o código acima não funciona bem. Como exemplo da vida real, podemos usar o Faker . O código acima relata que é iterável, mas realmente tentar iterá-lo causa um AttributeError(testado com o Faker 4.0.2):
>>>from faker importFaker>>> fake =Faker()>>> iter(fake)# No exception, must be iterable<iterator object at 0x7f1c71db58d0>>>> list(fake)# OoopsTraceback(most recent call last):File"<stdin>", line 1,in<module>File"/home/.../site-packages/faker/proxy.py", line 59,in __getitem__return self._factory_map[locale.replace('-','_')]AttributeError:'int' object has no attribute 'replace'
Se usássemos insinstance(), não consideraríamos acidentalmente instâncias do Faker (ou quaisquer outros objetos que tenham apenas __getitem__):
As respostas anteriores comentaram que o uso iter()é mais seguro, pois o antigo modo de implementar a iteração no Python era baseado __getitem__e a isinstance()abordagem não detectaria isso. Isso pode ter sido verdade nas versões antigas do Python, mas com base nos meus testes exaustivos isinstance()funciona muito bem hoje em dia. O único caso em isinstance()que não funcionou, mas iter()funcionou, UserDictao usar o Python 2. Se isso for relevante, é possível usá isinstance(item, (Iterable, UserDict))-lo para obter isso.
Também typing.Dicté considerado iterável por, iter(Dict)mas list(Dict)falha com erro TypeError: Parameters to generic types must be types. Got 0.. Como esperado, isinstance(Dict, Iterable)retorna falso.
Pekka Klärck 16/04
1
Cheguei à mesma conclusão, mas por razões diferentes. O uso iterfez com que alguns dos nossos códigos que usavam "pré-cache" desacelerassem desnecessariamente. Se o __iter__código for lento, será o mesmo que chamar iter... sempre que você quiser ver se algo é iterável.
thorwhalen
842
A verificação de __iter__trabalhos nos tipos de sequência, mas falharia em, por exemplo, seqüências de caracteres no Python 2 . Também gostaria de saber a resposta certa, até então, aqui está uma possibilidade (que funcionaria também em strings):
from __future__ import print_functiontry:
some_object_iterator = iter(some_object)exceptTypeErroras te:print(some_object,'is not iterable')
A iterverificação interna do __iter__método ou, no caso de seqüências de caracteres, o __getitem__método.
Outra abordagem pitônica geral é assumir uma iterável e falhar normalmente se não funcionar no objeto especificado. O glossário Python:
Estilo de programação Python que determina o tipo de um objeto pela inspeção de seu método ou assinatura de atributo, e não por um relacionamento explícito com algum objeto de tipo ("Se ele se parece com um pato e grasna como um pato , deve ser um pato .") Enfatizando as interfaces em vez de tipos específicos, o código bem projetado aprimora sua flexibilidade, permitindo a substituição polimórfica. A digitação de pato evita testes usando type () ou isinstance (). Em vez disso, normalmente emprega o estilo de programação EAFP (mais fácil pedir perdão do que permissão).
...
try:
_ =(e for e in my_object)exceptTypeError:print my_object,'is not iterable'
O collectionsmódulo fornece algumas classes base abstratas, que permitem perguntar a classes ou instâncias se elas fornecem funcionalidade específica, por exemplo:
from collections.abc importIterableif isinstance(e,Iterable):# e is iterable
No entanto, isso não verifica as classes que são iteráveis __getitem__.
[e for e in my_object]pode gerar uma exceção por outros motivos, ou seja, my_objecté indefinido ou possível erro na my_objectimplementação.
Nick Dandoulakis
37
Uma string é uma sequência ( isinstance('', Sequence) == True) e, como qualquer sequência, é iterável ( isinstance('', Iterable)). Embora hasattr('', '__iter__') == Falsee possa ser confuso.
JFS
82
Se my_objectfor muito grande (digamos, infinito itertools.count()), a compreensão da sua lista consumirá muito tempo / memória. Melhor criar um gerador, que nunca tentará criar uma lista (potencialmente infinita).
31711 Chris Lutz
14
E se some_object lançar TypeError causado por outro motivo (bugs etc.) também? Como podemos diferenciá-lo do "Não é iterável TypeError"?
Shaung 13/09/11
54
Observe que no Python 3: hasattr(u"hello", '__iter__')retornaTrue
Carlos
572
Digitação de pato
try:
iterator = iter(theElement)exceptTypeError:# not iterableelse:# iterable# for obj in iterator:# pass
Verificação de tipo
Use as classes base abstratas . Eles precisam pelo menos do Python 2.6 e funcionam apenas para classes de novo estilo.
from collections.abc importIterable# import directly from collections for Python < 3.3if isinstance(theElement,Iterable):# iterableelse:# not iterable
No entanto, iter()é um pouco mais confiável, conforme descrito na documentação :
A verificação isinstance(obj, Iterable)detecta classes que são registradas como Iterable ou que possuem um __iter__()método, mas não detecta classes que iteram com o __getitem__()
método. A única maneira confiável de determinar se um objeto é iterável é chamar iter(obj).
De "Fluent Python", de Luciano Ramalho: A partir do Python 3.4, a maneira mais precisa de verificar se um objeto x é iterável é chamar iter (x) e manipular uma exceção TypeError, se não for. Isso é mais preciso do que usar isinstance (x, abc.Iterable), porque o iter (x) também considera o método getitem herdado , enquanto o Iterable ABC não.
RdB
Caso você esteja pensando "ah, vou apenas em isinstance(x, (collections.Iterable, collections.Sequence))vez de iter(x)", observe que isso ainda não detectará um objeto iterável que implemente apenas, __getitem__mas não __len__. Use iter(x)e pegue a exceção.
Dale
Sua segunda resposta não funciona. Em PyUNO, se eu fizer iter(slide1), tudo vai bem, no entanto, os isinstance(slide1, Iterable)lances TypeError: issubclass() arg 1 must be a class.
Hi-Angel
@ Hi-Angel soa como um bug no PyUNOObserve que sua mensagem de erro diz em issubclass()vez de isinstance().
Georg Schölly 23/07/19
2
Chamar iter () sobre um objeto pode ser uma operação cara (consulte DataLoader no Pytorch, que bifurca / gera vários processos no iter ()).
szali
126
Eu gostaria de esclarecer um pouco mais de luz sobre a interação de iter, __iter__e __getitem__eo que acontece por trás das cortinas. Armado com esse conhecimento, você será capaz de entender por que o melhor que você pode fazer é
try:
iter(maybe_iterable)print('iteration will probably work')exceptTypeError:print('not iterable')
Vou listar os fatos primeiro e depois acompanhar com um lembrete rápido do que acontece quando você emprega um forloop em python, seguido de uma discussão para ilustrar os fatos.
Fatos
Você pode obter um iterador de qualquer objeto ochamando iter(o)se pelo menos uma das seguintes condições for verdadeira:
a) opossui um __iter__método que retorna um objeto iterador. Um iterador é qualquer objeto com um método __iter__e __next__(Python 2 next:).
b) otem um __getitem__método
Verificar uma instância de Iterableou Sequenceou verificar o atributo __iter__não é suficiente.
Se um objeto oimplementar apenas __getitem__, mas não __iter__, iter(o)construirá um iterador que tenta buscar itens opelo índice inteiro, iniciando no índice 0. O iterador capturará qualquer IndexError(mas nenhum outro erro) gerado e, em seguida, se elevará StopIteration.
No sentido mais geral, não há como verificar se o iterador retornado iteré sensato, exceto para testá-lo.
Se um objeto for oimplementado __iter__, a iterfunção garantirá que o objeto retornado por __iter__seja um iterador. Não há verificação de integridade se um objeto é implementado apenas __getitem__.
__iter__vitórias. Se um objeto oimplementa ambos __iter__e __getitem__, iter(o)será chamado __iter__.
Se você deseja tornar seus próprios objetos iteráveis, sempre implemente o __iter__método
for rotações
Para acompanhar, você precisa entender o que acontece quando você emprega um forloop no Python. Sinta-se livre para pular para a próxima seção, se você já sabe.
Quando você usa for item in opara algum objeto iterável o, o Python chama iter(o)e espera um objeto iterador como o valor de retorno. Um iterador é qualquer objeto que implementa um método __next__(ou nextno Python 2) e um __iter__método.
Por convenção, o __iter__método de um iterador deve retornar o próprio objeto (ou seja return self). O Python então chama nexto iterador até que StopIterationseja gerado. Tudo isso acontece implicitamente, mas a seguinte demonstração o torna visível:
import random
classDemoIterable(object):def __iter__(self):print('__iter__ called')returnDemoIterator()classDemoIterator(object):def __iter__(self):return self
def __next__(self):print('__next__ called')
r = random.randint(1,10)if r ==5:print('raising StopIteration')raiseStopIterationreturn r
Iteração sobre um DemoIterable:
>>> di =DemoIterable()>>>for x in di:...print(x)...
__iter__ called
__next__ called
9
__next__ called
8
__next__ called
10
__next__ called
3
__next__ called
10
__next__ called
raising StopIteration
Discussão e ilustrações
Nos pontos 1 e 2: obtendo um iterador e verificações não confiáveis
É por isso que o Fluent Python de Luciano Ramalho recomenda chamar itere manipular o potencial TypeErrorcomo a maneira mais precisa de verificar se um objeto é iterável. Citando diretamente do livro:
No Python 3.4, a maneira mais precisa de verificar se um objeto xé iterável é chamar iter(x)e manipular uma TypeErrorexceção, se não for. Isso é mais preciso do que usar isinstance(x, abc.Iterable), porque iter(x)também considera o __getitem__método legado , enquanto o IterableABC não.
No ponto 3: Iterando sobre objetos que fornecem apenas __getitem__, mas não__iter__
Iterando sobre uma instância de BasicIterableobras conforme o esperado: Python constrói um iterador que tenta buscar itens por índice, começando em zero, até que um IndexErrorseja gerado. O __getitem__método do objeto demo simplesmente retorna o itemque foi fornecido como argumento __getitem__(self, item)pelo iterador retornado por iter.
>>> b =BasicIterable()>>> it = iter(b)>>> next(it)0>>> next(it)1>>> next(it)2>>> next(it)Traceback(most recent call last):File"<stdin>", line 1,in<module>StopIteration
Observe que o iterador dispara StopIterationquando não pode retornar o próximo item e que o IndexErrorque é levantado item == 3é tratado internamente. É por isso que repetir BasicIterableum forloop com um loop funciona conforme o esperado:
>>>for x in b:...print(x)...012
Aqui está outro exemplo para esclarecer o conceito de como o iterador retornou itertentando acessar itens por índice. WrappedDictnão herda dict, o que significa que as instâncias não terão um __iter__método.
classWrappedDict(object):# note: no inheritance from dict!def __init__(self, dic):
self._dict = dic
def __getitem__(self, item):try:return self._dict[item]# delegate to dict.__getitem__exceptKeyError:raiseIndexError
Observe que as chamadas para __getitem__são delegadas dict.__getitem__para as quais a notação de colchete é simplesmente uma abreviação.
>>> w =WrappedDict({-1:'not printed',...0:'hi',1:'StackOverflow',2:'!',...4:'not printed',...'x':'not printed'})>>>for x in w:...print(x)...
hi
StackOverflow!
Nos pontos 4 e 5: iterverifica um iterador quando ele chama__iter__ :
Quando iter(o)é chamado para um objeto o, itergarante que o valor de retorno de __iter__, se o método estiver presente, seja um iterador. Isso significa que o objeto retornado deve implementar __next__(ou nextno Python 2) e __iter__. iternão pode executar nenhuma verificação de integridade para objetos que apenas fornecem __getitem__, porque não há como verificar se os itens do objeto estão acessíveis pelo índice inteiro.
classFailIterIterable(object):def __iter__(self):return object()# not an iteratorclassFailGetitemIterable(object):def __getitem__(self, item):raiseException
Observe que a construção de um iterador a partir de FailIterIterableinstâncias falha imediatamente, enquanto a construção de um iterador FailGetItemIterableé bem - sucedida, mas lança uma exceção na primeira chamada para __next__.
>>> fii =FailIterIterable()>>> iter(fii)Traceback(most recent call last):File"<stdin>", line 1,in<module>TypeError: iter() returned non-iterator of type 'object'>>>>>> fgi =FailGetitemIterable()>>> it = iter(fgi)>>> next(it)Traceback(most recent call last):File"<stdin>", line 1,in<module>File"/path/iterdemo.py", line 42,in __getitem__
raiseExceptionException
No ponto 6: __iter__vitórias
Este é direto. Se um objeto implementa __iter__e __getitem__, iterserá chamado __iter__. Considere a seguinte classe
>>> iwd =IterWinsDemo()>>>for x in iwd:...print(x)...
__iter__
wins
No ponto 7: suas classes iteráveis devem implementar __iter__
Você pode se perguntar por que a maioria das seqüências internas, como listimplementar um __iter__método, __getitem__seria suficiente.
classWrappedList(object):# note: no inheritance from list!def __init__(self, lst):
self._list = lst
def __getitem__(self, item):return self._list[item]
Afinal, iteração sobre as instâncias da classe acima, que delega chamadas para __getitem__a list.__getitem__(usando a notação colchete), vai funcionar bem:
>>> wl =WrappedList(['A','B','C'])>>>for x in wl:...print(x)...
A
B
C
Os motivos pelos quais os iterables personalizados devem implementar __iter__são os seguintes:
Se você implementar __iter__, as instâncias serão consideradas iteráveis e isinstance(o, collections.abc.Iterable)retornarão True.
Se o objeto retornado por __iter__não for um iterador, iterfalhará imediatamente e aumentará a TypeError.
O tratamento especial de __getitem__existe por motivos de compatibilidade com versões anteriores. Citando novamente o Fluent Python:
É por isso que qualquer sequência Python é iterável: todas elas são implementadas __getitem__. De fato, as seqüências padrão também são implementadas __iter__, e a sua também, porque o tratamento especial de __getitem__existe por razões de compatibilidade com versões anteriores e pode desaparecer no futuro (embora não seja preterido enquanto escrevo isso).
portanto, é seguro definir um predicado is_iterableretornando Trueno trybloco e Falseno except TypeErrorbloco?
Alancalvitti
Esta é uma ótima resposta. Eu acho que destaca a natureza não intuitiva e infeliz do protocolo getitem. Nunca deveria ter sido adicionado.
Neil G
31
Isso não é suficiente: o objeto retornado por __iter__deve implementar o protocolo de iteração (ou seja, nextmétodo). Veja a seção relevante na documentação .
No Python, uma boa prática é "tentar ver" em vez de "verificar".
@willem: ou "não pedir permissão, mas por perdão" ;-)
jldupont
14
@willem Os estilos "permissão" e "perdão" são qualificados como digitação de pato. Se você perguntar o que um objeto pode fazer e não o que é , isso significa digitação de pato. Se você usa introspecção, isso é "permissão"; se você apenas tentar fazê-lo e ver se funciona ou não, isso é "perdão".
Mark Reed
22
No Python <= 2.5, você não pode e não deveria - iterável era uma interface "informal".
Mas, desde o Python 2.6 e 3.0, você pode aproveitar a nova infraestrutura ABC (abstract base class) juntamente com alguns ABCs internos disponíveis no módulo de coleções:
from collections importIterableclassMyObject(object):pass
mo =MyObject()print isinstance(mo,Iterable)Iterable.register(MyObject)print isinstance(mo,Iterable)print isinstance("abc",Iterable)
Agora, se isso é desejável ou realmente funciona, é apenas uma questão de convenções. Como você pode ver, você pode registrar um objeto não iterável como Iterable - e isso gerará uma exceção no tempo de execução. Portanto, isinstance adquire um significado "novo" - apenas verifica a compatibilidade do tipo "declarado", o que é um bom caminho a percorrer no Python.
Por outro lado, se seu objeto não satisfizer a interface que você precisa, o que você fará? Veja o seguinte exemplo:
from collections importIterablefrom traceback import print_exc
def check_and_raise(x):ifnot isinstance(x,Iterable):raiseTypeError,"%s is not iterable"% x
else:for i in x:print i
def just_iter(x):for i in x:print i
classNotIterable(object):passif __name__ =="__main__":try:
check_and_raise(5)except:
print_exc()printtry:
just_iter(5)except:
print_exc()printtry:Iterable.register(NotIterable)
ni =NotIterable()
check_and_raise(ni)except:
print_exc()print
Se o objeto não satisfizer o que você espera, basta lançar um TypeError, mas se o ABC adequado tiver sido registrado, sua verificação será inútil. Pelo contrário, se o __iter__método estiver disponível, o Python reconhecerá automaticamente o objeto dessa classe como iterável.
Portanto, se você apenas espera um iterável, itere sobre ele e esqueça. Por outro lado, se você precisar fazer coisas diferentes, dependendo do tipo de entrada, poderá achar a infraestrutura ABC bastante útil.
não use bare except:no código de exemplo para iniciantes. Promove más práticas.
JFS
JFS: Eu não, mas precisava passar por vários códigos de exceção e não queria capturar a exceção específica ... Acho que o objetivo desse código é bastante claro.
27712 Alan Franzoni
21
try:#treat object as iterableexceptTypeError, e:#object is not actually iterable
Não faça verificações para ver se o seu pato é realmente um pato para ver se é iterável ou não, trate-o como se fosse e reclame se não foi.
Tecnicamente, durante a iteração, seu cálculo pode dar um TypeErrore tirar você daqui, mas basicamente sim.
23711 Chris Lutz
6
@ William: Por favor, use timeit para realizar uma referência. As exceções do Python geralmente são mais rápidas que as instruções if. Eles podem seguir um caminho um pouco mais curto pelo intérprete.
23409 S.Lott
2
@ William: IronPython tem lentas (em comparação com CPython) exceções.
JFS
2
Uma tentativa de trabalho: a afirmação é realmente rápida. Portanto, se você tiver poucas exceções, tente exceto é rápido. Se você espera muitas exceções, "se" pode ser mais rápido.
Arne Babenhauserheide
2
O objeto de exceção não deve ser capturado adicionando " as e" depois em TypeErrorvez de adicionar " , e"?
hellogoodbye
21
Desde o Python 3.5, você pode usar o módulo de digitação da biblioteca padrão para coisas relacionadas ao tipo:
from typing importIterable...if isinstance(my_item,Iterable):print(True)
que basicamente verifica se o objeto implementa o inoperador.
Vantagens (nenhuma das outras soluções possui as três):
é uma expressão (funciona como um lambda , em oposição à tentativa ... exceto variante)
é (deve ser) implementado por todos os iterables, incluindo strings (ao contrário de __iter__)
funciona em qualquer Python> = 2.5
Notas:
a filosofia Python de "pedir perdão, não permissão" não funciona bem quando, por exemplo, em uma lista você tem iteráveis e não iteráveis e precisa tratar cada elemento de maneira diferente de acordo com seu tipo (tratando iteráveis em try e non- iterables sobre exceto iria funcionar, mas ele ficaria butt-feio e enganosa)
soluções para esse problema que tentam realmente iterar sobre o objeto (por exemplo, [x para x em obj]) para verificar se é iterável podem induzir penalidades significativas de desempenho para iteráveis grandes (especialmente se você só precisa dos primeiros elementos do iterável, por exemplo) e deve ser evitado
É mais curto (e não requer importações adicionais) sem perder a clareza: ter um método "contém" parece uma maneira natural de verificar se algo é uma coleção de objetos.
Vlad
46
Só porque algo pode conter algo não significa necessariamente que é iterável. Por exemplo, um usuário pode verificar se um ponto está em um cubo 3D, mas como você iteraria nesse objeto?
Casey Kuball
13
Isto está incorreto. Um iterável em si não suporta contém , pelo menos no Python 3.4.
quer
15
Você pode tentar o seguinte:
def iterable(a):try:(x for x in a)returnTrueexceptTypeError:returnFalse
Se pudermos criar um gerador que itere sobre ele (mas nunca use o gerador para não ocupar espaço), é iterável. Parece um tipo de coisa "duh". Por que você precisa determinar se uma variável é iterável em primeiro lugar?
@badp, o (x for x in a)just cria um gerador, ele não faz nenhuma iteração no a.
catchmeifyoutry 23/12/2009
5
Tentar é (x for x in a)precisamente equivalente a tentar iterator = iter(a)? Ou há alguns casos em que os dois são diferentes?
máximo
Não é for _ in a: breakmais direto? É mais lento?
23415 Mr_and_Mrs_D # 23/15
2
@Mr_and_Mrs_D é ruim se o objeto testado for um iterador que será iterado posteriormente (será 1 item curto, pois sua posição não pode ser redefinida), a criação de geradores de lixo não itera sobre o objeto, pois não é iterada, embora eu não tenha certeza de que isso aumentará 100% um TypeError se não for iterável.
todos os tipos de sequências (tais como list, stre tuple) e alguns tipos de sequências não como dicte filee objectos de todas as classes definidas com um __iter__()ou __getitem__()método. Os iteráveis podem ser usados em um loop for e em muitos outros lugares em que uma sequência é necessária (zip (), mapa (), ...). Quando um objeto iterável é passado como argumento para a função interna iter (), ele retorna um iterador para o objeto.
Obviamente, dado o estilo geral de codificação do Python com base no fato de que é "mais fácil pedir perdão do que permissão"., A expectativa geral é usar
try:for i in object_in_question:
do_something
exceptTypeError:
do_something_for_non_iterable
Mas se você precisar checá-lo explicitamente, poderá testar um iterável por hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__"). Você precisa verificar os dois, porque strs não possui um __iter__método (pelo menos não no Python 2, no Python 3) e porque os generatorobjetos não têm um __getitem__método.
Sempre que você gosta if x: return Trueelse: return False( xsendo booleano), pode escrever isso como return x. No seu caso, return isinstance(…)sem qualquer if.
Alfe
Como você reconhece que a solução da Alfe é melhor, por que você não editou sua resposta para simplesmente dizer isso? Em vez disso, você agora tem duas versões em sua resposta. Verbosidade desnecessária. Enviando uma edição para corrigir isso.
Home
2
Você deve pegar "TypeError" na linha `exceto: return False`. Capturar tudo é um padrão ruim.
Mariusz Jamro 03/07/2014
Saiba disso. Traduzi esse trecho de código da biblioteca NumPy, que usa a exceção genérica.
Fmonegaglia
Só porque um código é retirado do NumPy não significa que seja bom ... padrão ou não, o único momento em que tudo deve ser feito é se você estiver explicitamente manipulando erros dentro do seu programa.
no entanto, isso apenas verifica se existe __iter__e realmente não se importa com sequências e similares.
ead 3/03
4
Sempre me iludiu por que o python tem, callable(obj) -> boolmas não iterable(obj) -> bool...
certamente é mais fácil de fazer, hasattr(obj,'__call__')mesmo que seja mais lento.
Como quase todas as outras respostas recomendam o uso de try/ except TypeError, onde o teste de exceções geralmente é considerado uma prática inadequada em qualquer idioma, aqui está uma implementação da qual iterable(obj) -> booleu mais gosto e uso com frequência:
Pelo bem do python 2, usarei um lambda apenas para esse aumento extra de desempenho ...
(no python 3, não importa o que você usa para definir a função, deftem aproximadamente a mesma velocidade que lambda)
Observe que essa função é executada mais rapidamente para objetos com __iter__uma vez que não é testada __getitem__.
A maioria dos objetos iteráveis deve depender de __iter__onde os objetos de caso especial se enquadram __getitem__, embora ambos sejam necessários para que um objeto seja iterável.
(e, como isso é padrão, também afeta objetos C)
ele não fornece código de trabalho, muito menos fala sobre desempenho de python ... embora essa resposta tenha sido realmente apenas por conveniência, como eu já vi várias vezes aqui.
Tcll
3
def is_iterable(x):try:0in x
exceptTypeError:returnFalseelse:returnTrue
Isso diz sim a todos os tipos de objetos iteráveis, mas diz não a seqüências de caracteres no Python 2 . (É o que eu quero, por exemplo, quando uma função recursiva pode pegar uma string ou um contêiner de strings. Nessa situação, pedir perdão pode levar ao obscurecimento, e é melhor pedir permissão primeiro.)
Nota: is_iterable () dirá sim para strings do tipo bytese bytearray.
bytes objetos no Python 3 são iteráveis True == is_iterable(b"string") == is_iterable("string".encode('utf-8')) Não existe esse tipo no Python 2.
bytearray objetos em Python 2 e 3 são iteráveis True == is_iterable(bytearray(b"abc"))
O OP hasattr(x, '__iter__')abordagem vai dizer sim para strings em Python 3 e não em Python 2 (não importa se ''ou b''ou u''). Graças a @LuisMasuelli por perceber, também o decepcionará __iter__.
A maneira mais fácil, respeitando a digitação do pato do Python , é capturar o erro (o Python sabe perfeitamente o que ele espera de um objeto para se tornar um iterador):
class A(object):def __getitem__(self, item):return something
class B(object):def __iter__(self):# Return a compliant iterator. Just an examplereturn iter([])class C(object):def __iter__(self):# Return crapreturn1class D(object):passdef iterable(obj):try:
iter(obj)returnTrueexcept:returnFalseassert iterable(A())assert iterable(B())assert iterable(C())assertnot iterable(D())
Notas :
É irrelevante a distinção entre o objeto não ser iterável ou um buggy __iter__ foi implementado, se o tipo de exceção for o mesmo: de qualquer maneira, você não poderá iterar o objeto.
Acho que entendo sua preocupação: como callableexiste uma verificação se eu também poderia confiar na digitação do pato para gerar uma AttributeErrorif __call__não definida para o meu objeto, mas esse não é o caso da verificação iterável?
Não sei a resposta, mas você pode implementar a função que eu (e outros usuários) fornecemos ou apenas capturar a exceção em seu código (sua implementação nessa parte será como a função que escrevi - apenas garanta que você isole o criação do iterador a partir do restante do código, para que você possa capturar a exceção e diferenciá-la de outra TypeError.
fruits =("apple","banana","peach")
isiterable(fruits)# returns True
num =345
isiterable(num)# returns False
isiterable(str)# returns False because str type is type class and it's not iterable.
hello ="hello dude !"
isiterable(hello)# returns True because as you know string objects are iterable
tantas respostas detalhadas acima com muitos upvotes e você joga em uma resposta inexplicável ... meh
Nrzonline
Por favor, não poste código nu. Inclua também uma explicação do que isso está fazendo.
Jonathan Mee
1
Em vez de verificar o __iter__atributo, você pode verificar o __len__atributo, que é implementado por cada python integrado no iterável, incluindo strings.
Objetos não iteráveis não implementariam isso por razões óbvias. No entanto, ele não captura iteráveis definidos pelo usuário que não o implementam, nem expressões geradoras com as quais iterpossa lidar. No entanto, isso pode ser feito em uma linha, e a adição de uma orverificação de expressão simples para geradores resolveria esse problema. (Observe que a escrita type(my_generator_expression) == generatorgeraria a NameError. Consulte esta resposta.)
Você pode usar o GeneratorType dos tipos:
>>>import types
>>> types.GeneratorType<class'generator'>>>> gen =(i for i in range(10))>>> isinstance(gen, types.GeneratorType)True
--- resposta aceita por utdemir
(Isso o torna útil para verificar se você pode chamar leno objeto.)
infelizmente, nem todos os objetos iteráveis usam __len__... nesse caso, geralmente é o uso inadequado do cálculo da distância entre dois objetos. onde obj.dist()poderia ser facilmente substituído.
Tcll
Sim. A maioria dos iterables definidos pelo usuário implementa iter e getitem, mas não o len. No entanto, os tipos internos fazem isso e, se você quiser verificar se pode chamar a função len, a verificação de len é mais segura. Mas você está certo.
DarthCadeus
0
Não é realmente "correto", mas pode servir como verificação rápida dos tipos mais comuns, como strings, tuplas, flutuadores, etc.
Meio atrasado para a festa, mas eu me fiz essa pergunta e vi isso, depois pensei em uma resposta. Não sei se alguém já postou isso. Mas, basicamente, notei que todos os tipos iteráveis têm __getitem __ () em seu ditado. É assim que você verifica se um objeto é iterável sem nem tentar. (Chalaça pretendida)
__getitem__
Também é suficiente para fazer um objeto iteráveliter(myObj)
bem - sucedido seisinstance(myObj, dict)
, portanto, se você estiver olhando para umamyObj
que possa ser uma sequência dedict
s ou uma únicadict
, terá êxito nos dois casos. Uma sutileza que é importante se você quiser saber o que é uma sequência e o que não é. (em Python 2)__getitem__
também é suficiente para tornar um objeto iterável ... se começar no índice zero .Respostas:
Ultimamente tenho estudado esse problema. Com base nisso, minha conclusão é que hoje em dia essa é a melhor abordagem:
O exposto acima já foi recomendado anteriormente, mas o consenso geral é de que o uso
iter()
seria melhor:Também usamos
iter()
nosso código para esse fim, mas ultimamente comecei a ficar cada vez mais irritado com objetos que apenas__getitem__
são considerados iteráveis. Há razões válidas para ter__getitem__
em um objeto não iterável e, com elas, o código acima não funciona bem. Como exemplo da vida real, podemos usar o Faker . O código acima relata que é iterável, mas realmente tentar iterá-lo causa umAttributeError
(testado com o Faker 4.0.2):Se usássemos
insinstance()
, não consideraríamos acidentalmente instâncias do Faker (ou quaisquer outros objetos que tenham apenas__getitem__
):As respostas anteriores comentaram que o uso
iter()
é mais seguro, pois o antigo modo de implementar a iteração no Python era baseado__getitem__
e aisinstance()
abordagem não detectaria isso. Isso pode ter sido verdade nas versões antigas do Python, mas com base nos meus testes exaustivosisinstance()
funciona muito bem hoje em dia. O único caso emisinstance()
que não funcionou, masiter()
funcionou,UserDict
ao usar o Python 2. Se isso for relevante, é possível usáisinstance(item, (Iterable, UserDict))
-lo para obter isso.fonte
typing.Dict
é considerado iterável por,iter(Dict)
maslist(Dict)
falha com erroTypeError: Parameters to generic types must be types. Got 0.
. Como esperado,isinstance(Dict, Iterable)
retorna falso.iter
fez com que alguns dos nossos códigos que usavam "pré-cache" desacelerassem desnecessariamente. Se o__iter__
código for lento, será o mesmo que chamariter
... sempre que você quiser ver se algo é iterável.A verificação de
__iter__
trabalhos nos tipos de sequência, mas falharia em, por exemplo, seqüências de caracteres no Python 2 . Também gostaria de saber a resposta certa, até então, aqui está uma possibilidade (que funcionaria também em strings):A
iter
verificação interna do__iter__
método ou, no caso de seqüências de caracteres, o__getitem__
método.Outra abordagem pitônica geral é assumir uma iterável e falhar normalmente se não funcionar no objeto especificado. O glossário Python:
O
collections
módulo fornece algumas classes base abstratas, que permitem perguntar a classes ou instâncias se elas fornecem funcionalidade específica, por exemplo:No entanto, isso não verifica as classes que são iteráveis
__getitem__
.fonte
[e for e in my_object]
pode gerar uma exceção por outros motivos, ou seja,my_object
é indefinido ou possível erro namy_object
implementação.isinstance('', Sequence) == True
) e, como qualquer sequência, é iterável (isinstance('', Iterable)
). Emborahasattr('', '__iter__') == False
e possa ser confuso.my_object
for muito grande (digamos, infinitoitertools.count()
), a compreensão da sua lista consumirá muito tempo / memória. Melhor criar um gerador, que nunca tentará criar uma lista (potencialmente infinita).hasattr(u"hello", '__iter__')
retornaTrue
Digitação de pato
Verificação de tipo
Use as classes base abstratas . Eles precisam pelo menos do Python 2.6 e funcionam apenas para classes de novo estilo.
No entanto,
iter()
é um pouco mais confiável, conforme descrito na documentação :fonte
isinstance(x, (collections.Iterable, collections.Sequence))
vez deiter(x)
", observe que isso ainda não detectará um objeto iterável que implemente apenas,__getitem__
mas não__len__
. Useiter(x)
e pegue a exceção.iter(slide1)
, tudo vai bem, no entanto, osisinstance(slide1, Iterable)
lancesTypeError: issubclass() arg 1 must be a class
.PyUNO
Observe que sua mensagem de erro diz emissubclass()
vez deisinstance()
.Eu gostaria de esclarecer um pouco mais de luz sobre a interação de
iter
,__iter__
e__getitem__
eo que acontece por trás das cortinas. Armado com esse conhecimento, você será capaz de entender por que o melhor que você pode fazer éVou listar os fatos primeiro e depois acompanhar com um lembrete rápido do que acontece quando você emprega um
for
loop em python, seguido de uma discussão para ilustrar os fatos.Fatos
Você pode obter um iterador de qualquer objeto
o
chamandoiter(o)
se pelo menos uma das seguintes condições for verdadeira:a)
o
possui um__iter__
método que retorna um objeto iterador. Um iterador é qualquer objeto com um método__iter__
e__next__
(Python 2next
:).b)
o
tem um__getitem__
métodoVerificar uma instância de
Iterable
ouSequence
ou verificar o atributo__iter__
não é suficiente.Se um objeto
o
implementar apenas__getitem__
, mas não__iter__
,iter(o)
construirá um iterador que tenta buscar itenso
pelo índice inteiro, iniciando no índice 0. O iterador capturará qualquerIndexError
(mas nenhum outro erro) gerado e, em seguida, se elevaráStopIteration
.No sentido mais geral, não há como verificar se o iterador retornado
iter
é sensato, exceto para testá-lo.Se um objeto for
o
implementado__iter__
, aiter
função garantirá que o objeto retornado por__iter__
seja um iterador. Não há verificação de integridade se um objeto é implementado apenas__getitem__
.__iter__
vitórias. Se um objetoo
implementa ambos__iter__
e__getitem__
,iter(o)
será chamado__iter__
.Se você deseja tornar seus próprios objetos iteráveis, sempre implemente o
__iter__
métodofor
rotaçõesPara acompanhar, você precisa entender o que acontece quando você emprega um
for
loop no Python. Sinta-se livre para pular para a próxima seção, se você já sabe.Quando você usa
for item in o
para algum objeto iterávelo
, o Python chamaiter(o)
e espera um objeto iterador como o valor de retorno. Um iterador é qualquer objeto que implementa um método__next__
(ounext
no Python 2) e um__iter__
método.Por convenção, o
__iter__
método de um iterador deve retornar o próprio objeto (ou sejareturn self
). O Python então chamanext
o iterador até queStopIteration
seja gerado. Tudo isso acontece implicitamente, mas a seguinte demonstração o torna visível:Iteração sobre um
DemoIterable
:Discussão e ilustrações
Nos pontos 1 e 2: obtendo um iterador e verificações não confiáveis
Considere a seguinte classe:
Chamar
iter
com uma instância deBasicIterable
retornará um iterador sem problemas, porqueBasicIterable
implementa__getitem__
.No entanto, é importante observar que
b
não possui o__iter__
atributo e não é considerado uma instânciaIterable
ouSequence
:É por isso que o Fluent Python de Luciano Ramalho recomenda chamar
iter
e manipular o potencialTypeError
como a maneira mais precisa de verificar se um objeto é iterável. Citando diretamente do livro:No ponto 3: Iterando sobre objetos que fornecem apenas
__getitem__
, mas não__iter__
Iterando sobre uma instância de
BasicIterable
obras conforme o esperado: Python constrói um iterador que tenta buscar itens por índice, começando em zero, até que umIndexError
seja gerado. O__getitem__
método do objeto demo simplesmente retorna oitem
que foi fornecido como argumento__getitem__(self, item)
pelo iterador retornado poriter
.Observe que o iterador dispara
StopIteration
quando não pode retornar o próximo item e que oIndexError
que é levantadoitem == 3
é tratado internamente. É por isso que repetirBasicIterable
umfor
loop com um loop funciona conforme o esperado:Aqui está outro exemplo para esclarecer o conceito de como o iterador retornou
iter
tentando acessar itens por índice.WrappedDict
não herdadict
, o que significa que as instâncias não terão um__iter__
método.Observe que as chamadas para
__getitem__
são delegadasdict.__getitem__
para as quais a notação de colchete é simplesmente uma abreviação.Nos pontos 4 e 5:
iter
verifica um iterador quando ele chama__iter__
:Quando
iter(o)
é chamado para um objetoo
,iter
garante que o valor de retorno de__iter__
, se o método estiver presente, seja um iterador. Isso significa que o objeto retornado deve implementar__next__
(ounext
no Python 2) e__iter__
.iter
não pode executar nenhuma verificação de integridade para objetos que apenas fornecem__getitem__
, porque não há como verificar se os itens do objeto estão acessíveis pelo índice inteiro.Observe que a construção de um iterador a partir de
FailIterIterable
instâncias falha imediatamente, enquanto a construção de um iteradorFailGetItemIterable
é bem - sucedida, mas lança uma exceção na primeira chamada para__next__
.No ponto 6:
__iter__
vitóriasEste é direto. Se um objeto implementa
__iter__
e__getitem__
,iter
será chamado__iter__
. Considere a seguinte classee a saída ao fazer um loop sobre uma instância:
No ponto 7: suas classes iteráveis devem implementar
__iter__
Você pode se perguntar por que a maioria das seqüências internas, como
list
implementar um__iter__
método,__getitem__
seria suficiente.Afinal, iteração sobre as instâncias da classe acima, que delega chamadas para
__getitem__
alist.__getitem__
(usando a notação colchete), vai funcionar bem:Os motivos pelos quais os iterables personalizados devem implementar
__iter__
são os seguintes:__iter__
, as instâncias serão consideradas iteráveis eisinstance(o, collections.abc.Iterable)
retornarãoTrue
.__iter__
não for um iterador,iter
falhará imediatamente e aumentará aTypeError
.__getitem__
existe por motivos de compatibilidade com versões anteriores. Citando novamente o Fluent Python:fonte
is_iterable
retornandoTrue
notry
bloco eFalse
noexcept TypeError
bloco?Isso não é suficiente: o objeto retornado por
__iter__
deve implementar o protocolo de iteração (ou seja,next
método). Veja a seção relevante na documentação .No Python, uma boa prática é "tentar ver" em vez de "verificar".
fonte
No Python <= 2.5, você não pode e não deveria - iterável era uma interface "informal".
Mas, desde o Python 2.6 e 3.0, você pode aproveitar a nova infraestrutura ABC (abstract base class) juntamente com alguns ABCs internos disponíveis no módulo de coleções:
Agora, se isso é desejável ou realmente funciona, é apenas uma questão de convenções. Como você pode ver, você pode registrar um objeto não iterável como Iterable - e isso gerará uma exceção no tempo de execução. Portanto, isinstance adquire um significado "novo" - apenas verifica a compatibilidade do tipo "declarado", o que é um bom caminho a percorrer no Python.
Por outro lado, se seu objeto não satisfizer a interface que você precisa, o que você fará? Veja o seguinte exemplo:
Se o objeto não satisfizer o que você espera, basta lançar um TypeError, mas se o ABC adequado tiver sido registrado, sua verificação será inútil. Pelo contrário, se o
__iter__
método estiver disponível, o Python reconhecerá automaticamente o objeto dessa classe como iterável.Portanto, se você apenas espera um iterável, itere sobre ele e esqueça. Por outro lado, se você precisar fazer coisas diferentes, dependendo do tipo de entrada, poderá achar a infraestrutura ABC bastante útil.
fonte
except:
no código de exemplo para iniciantes. Promove más práticas.Não faça verificações para ver
se o seu pato é realmente um patopara ver se é iterável ou não, trate-o como se fosse e reclame se não foi.fonte
TypeError
e tirar você daqui, mas basicamente sim.as e
" depois emTypeError
vez de adicionar ", e
"?Desde o Python 3.5, você pode usar o módulo de digitação da biblioteca padrão para coisas relacionadas ao tipo:
fonte
A melhor solução que encontrei até agora:
hasattr(obj, '__contains__')
que basicamente verifica se o objeto implementa o
in
operador.Vantagens (nenhuma das outras soluções possui as três):
__iter__
)Notas:
fonte
Você pode tentar o seguinte:
Se pudermos criar um gerador que itere sobre ele (mas nunca use o gerador para não ocupar espaço), é iterável. Parece um tipo de coisa "duh". Por que você precisa determinar se uma variável é iterável em primeiro lugar?
fonte
iterable(itertools.repeat(0))
? :)(x for x in a)
just cria um gerador, ele não faz nenhuma iteração no a.(x for x in a)
precisamente equivalente a tentariterator = iter(a)
? Ou há alguns casos em que os dois são diferentes?for _ in a: break
mais direto? É mais lento?Encontrei uma boa solução aqui :
fonte
De acordo com o Glossário Python 2 , iteráveis são
Obviamente, dado o estilo geral de codificação do Python com base no fato de que é "mais fácil pedir perdão do que permissão"., A expectativa geral é usar
Mas se você precisar checá-lo explicitamente, poderá testar um iterável por
hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__")
. Você precisa verificar os dois, porquestr
s não possui um__iter__
método (pelo menos não no Python 2, no Python 3) e porque osgenerator
objetos não têm um__getitem__
método.fonte
Costumo achar conveniente, dentro dos meus scripts, definir uma
iterable
função. (Agora incorpora a simplificação sugerida por Alfe):para que você possa testar se algum objeto é iterável da forma mais legível
como você faria com a
callable
funçãoEDIT: se você tiver o numpy instalado, você pode simplesmente fazer: from
numpy import iterable
, que é simplesmente algo comoSe você não tiver um numpy, pode simplesmente implementar esse código, ou o código acima.
fonte
if x: return True
else: return False
(x
sendo booleano), pode escrever isso comoreturn x
. No seu caso,return isinstance(…)
sem qualquerif
.pandas tem uma função interna assim:
fonte
__iter__
e realmente não se importa com sequências e similares.Sempre me iludiu por que o python tem,
callable(obj) -> bool
mas nãoiterable(obj) -> bool
...certamente é mais fácil de fazer,
hasattr(obj,'__call__')
mesmo que seja mais lento.Como quase todas as outras respostas recomendam o uso de
try
/except TypeError
, onde o teste de exceções geralmente é considerado uma prática inadequada em qualquer idioma, aqui está uma implementação da qualiterable(obj) -> bool
eu mais gosto e uso com frequência:Pelo bem do python 2, usarei um lambda apenas para esse aumento extra de desempenho ...
(no python 3, não importa o que você usa para definir a função,
def
tem aproximadamente a mesma velocidade quelambda
)Observe que essa função é executada mais rapidamente para objetos com
__iter__
uma vez que não é testada__getitem__
.A maioria dos objetos iteráveis deve depender de
__iter__
onde os objetos de caso especial se enquadram__getitem__
, embora ambos sejam necessários para que um objeto seja iterável.(e, como isso é padrão, também afeta objetos C)
fonte
Isso diz sim a todos os tipos de objetos iteráveis, mas diz não a seqüências de caracteres no Python 2 . (É o que eu quero, por exemplo, quando uma função recursiva pode pegar uma string ou um contêiner de strings. Nessa situação, pedir perdão pode levar ao obscurecimento, e é melhor pedir permissão primeiro.)
Muitas outras estratégias aqui dirão sim às seqüências de caracteres. Use-os se é isso que você deseja.
Nota: is_iterable () dirá sim para strings do tipo
bytes
ebytearray
.bytes
objetos no Python 3 são iteráveisTrue == is_iterable(b"string") == is_iterable("string".encode('utf-8'))
Não existe esse tipo no Python 2.bytearray
objetos em Python 2 e 3 são iteráveisTrue == is_iterable(bytearray(b"abc"))
O OP
hasattr(x, '__iter__')
abordagem vai dizer sim para strings em Python 3 e não em Python 2 (não importa se''
oub''
ouu''
). Graças a @LuisMasuelli por perceber, também o decepcionará__iter__
.fonte
A maneira mais fácil, respeitando a digitação do pato do Python , é capturar o erro (o Python sabe perfeitamente o que ele espera de um objeto para se tornar um iterador):
Notas :
__iter__
foi implementado, se o tipo de exceção for o mesmo: de qualquer maneira, você não poderá iterar o objeto.Acho que entendo sua preocupação: como
callable
existe uma verificação se eu também poderia confiar na digitação do pato para gerar umaAttributeError
if__call__
não definida para o meu objeto, mas esse não é o caso da verificação iterável?Não sei a resposta, mas você pode implementar a função que eu (e outros usuários) fornecemos ou apenas capturar a exceção em seu código (sua implementação nessa parte será como a função que escrevi - apenas garanta que você isole o criação do iterador a partir do restante do código, para que você possa capturar a exceção e diferenciá-la de outra
TypeError
.fonte
A
isiterable
função no código a seguir retornaTrue
se o objeto for iterável. se não for retornos iteráveisFalse
exemplo
fonte
Em vez de verificar o
__iter__
atributo, você pode verificar o__len__
atributo, que é implementado por cada python integrado no iterável, incluindo strings.Objetos não iteráveis não implementariam isso por razões óbvias. No entanto, ele não captura iteráveis definidos pelo usuário que não o implementam, nem expressões geradoras com as quais
iter
possa lidar. No entanto, isso pode ser feito em uma linha, e a adição de umaor
verificação de expressão simples para geradores resolveria esse problema. (Observe que a escritatype(my_generator_expression) == generator
geraria aNameError
. Consulte esta resposta.)(Isso o torna útil para verificar se você pode chamar
len
o objeto.)fonte
__len__
... nesse caso, geralmente é o uso inadequado do cálculo da distância entre dois objetos. ondeobj.dist()
poderia ser facilmente substituído.Não é realmente "correto", mas pode servir como verificação rápida dos tipos mais comuns, como strings, tuplas, flutuadores, etc.
fonte
Meio atrasado para a festa, mas eu me fiz essa pergunta e vi isso, depois pensei em uma resposta. Não sei se alguém já postou isso. Mas, basicamente, notei que todos os tipos iteráveis têm __getitem __ () em seu ditado. É assim que você verifica se um objeto é iterável sem nem tentar. (Chalaça pretendida)
fonte