Tenho uma função que recebe um argumento que pode ser um item único ou um item duplo:
def iterable(arg)
if #arg is an iterable:
print "yes"
else:
print "no"
de modo a:
>>> iterável (("f", "f")) sim >>> iterável (["f", "f"]) sim >>> iterável ("ff") não
O problema é que a string é tecnicamente iterável, então não posso simplesmente capturar o ValueError ao tentar arg[1]
. Não quero usar isinstance (), porque essa não é uma boa prática (ou pelo menos foi o que me disseram).
isinstance
é a maneira de fazer isso em linguagens com tipos dinâmicos. Uma coisa que não deve ser usada todos os dias, mas OK em casos justificados.Respostas:
Use isinstance (não vejo por que é uma prática ruim)
import types if not isinstance(arg, types.StringTypes):
Observe o uso de StringTypes. Isso garante que não nos esqueçamos de algum tipo obscuro de string.
Por outro lado, isso também funciona para classes de string derivadas.
class MyString(str): pass isinstance(MyString(" "), types.StringTypes) # true
Além disso, você pode querer dar uma olhada nesta questão anterior .
Felicidades.
NB: o comportamento mudou no Python 3
StringTypes
ebasestring
não está mais definido. Dependendo de suas necessidades, você pode substituí-losisinstance
porstr
, ou uma tupla de subconjunto de(str, bytes, unicode)
, por exemplo, para usuários Cython. Como @Theron Luhn mencionou, você também pode usarsix
.fonte
isinstance
pode ser a única maneira.do isinstance(arg, str)
. Para uma versão compatível com versões anteriores, considere usar pythonhosted.org/six/#six.string_typestypes.StringTypes
não está disponível em Python3. Qual é o valor em Python2?A partir de 2017, aqui está uma solução portátil que funciona com todas as versões do Python:
#!/usr/bin/env python import collections import six def iterable(arg): return ( isinstance(arg, collections.Iterable) and not isinstance(arg, six.string_types) ) # non-string iterables assert iterable(("f", "f")) # tuple assert iterable(["f", "f"]) # list assert iterable(iter("ff")) # iterator assert iterable(range(44)) # generator assert iterable(b"ff") # bytes (Python 2 calls this a string) # strings or non-iterables assert not iterable(u"ff") # string assert not iterable(44) # integer assert not iterable(iterable) # function
fonte
Desde Python 2.6, com a introdução de classes de base abstratas
isinstance
(usadas em ABCs, não em classes concretas) agora é considerado perfeitamente aceitável. Especificamente:from abc import ABCMeta, abstractmethod class NonStringIterable: __metaclass__ = ABCMeta @abstractmethod def __iter__(self): while False: yield None @classmethod def __subclasshook__(cls, C): if cls is NonStringIterable: if any("__iter__" in B.__dict__ for B in C.__mro__): return True return NotImplemented
Esta é uma cópia exata (mudando apenas o nome da classe) de
Iterable
conforme definido em_abcoll.py
(um detalhe de implementação decollections.py
) ... a razão pela qual isso funciona como você deseja, enquantocollections.Iterable
não funciona, é que o último vai além para garantir que as strings sejam considerado iterável, chamandoIterable.register(str)
explicitamente logo após essaclass
instrução.Claro que é fácil aumentar
__subclasshook__
retornandoFalse
antes daany
chamada para outras classes que você deseja excluir especificamente de sua definição.Em qualquer caso, depois de ter importado este novo módulo como
myiter
,isinstance('ciao', myiter.NonStringIterable)
seráFalse
eisinstance([1,2,3], myiter.NonStringIterable)
seráTrue
, exatamente como você solicitou - e no Python 2.6 e posteriores esta é considerada a maneira adequada de incorporar tais verificações ... defina uma classe base abstrata e verifiqueisinstance
isso.fonte
isinstance('spam', NonStringIterable)
retornaTrue
.__iter__
está implementado em strings no Python 3. Portanto, meu parágrafo "fácil de aumentar" torna-se aplicável e, por exemplo,if issublass(cls, str): return False
precisa ser adicionado no início de__subclasshook__
(bem como quaisquer outras classes que definam,__iter__
mas em seu mentalidade não deve ser aceita como "iteráveis não-string").if issublass(C, str): return False
deve ser adicionado?Sei que este é um post antigo, mas pensei que valeria a pena adicionar minha abordagem para a posteridade da Internet. A função abaixo parece funcionar para mim na maioria das circunstâncias com Python 2 e 3:
def is_collection(obj): """ Returns true for any iterable which is not a string or byte sequence. """ try: if isinstance(obj, unicode): return False except NameError: pass if isinstance(obj, bytes): return False try: iter(obj) except TypeError: return False try: hasattr(None, obj) except TypeError: return True return False
Isso verifica se há uma string não iterável por (mis) usando o embutido
hasattr
que irá gerar aTypeError
quando seu segundo argumento não for uma string ou uma string Unicode.fonte
Ao combinar as respostas anteriores, estou usando:
import types import collections #[...] if isinstance(var, types.StringTypes ) \ or not isinstance(var, collections.Iterable): #[Do stuff...]
Não é 100% à prova de idiotas, mas se um objeto não for iterável, você ainda pode deixá-lo passar e voltar a digitar.
Editar: Python3
types.StringTypes == (str, unicode)
. O equivalente Phython3 é:if isinstance(var, str ) \ or not isinstance(var, collections.Iterable):
fonte
2.x
Eu teria sugerido:
hasattr(x, '__iter__')
ou em vista do comentário de David Charles ajustando isso para Python3, que tal:
hasattr(x, '__iter__') and not isinstance(x, (str, bytes))
3.x
o
basestring
tipo abstrato embutido foi removido . Use em seustr
lugar. Os tiposstr
ebytes
não têm funcionalidade suficiente em comum para garantir uma classe base compartilhada.fonte
__iter__
existe strings no Python 3?Como você observou corretamente, uma única string é uma sequência de caracteres.
Portanto, o que você realmente deseja fazer é descobrir que tipo de sequência
arg
é usando isinstance ou type (a) == str.Se você deseja realizar uma função que leva uma quantidade variável de parâmetros, você deve fazer assim:
def function(*args): # args is a tuple for arg in args: do_something(arg)
function ("ff") e function ("ff", "ff") funcionarão.
Não consigo ver um cenário em que uma função isiterable () como a sua seja necessária. Não é isinstance () que é um estilo ruim, mas situações em que você precisa usar isinstance ().
fonte
type(a) == str
deve ser evitado. É uma má prática porque não leva em consideração tipos semelhantes ou tipos derivados destr
.type
não sobe na hierarquia de tipo, masisinstance
sim, portanto, é melhor usarisinstance
.Para expandir explicitamente o excelente hack de Alex Martelli
collections.py
e abordar algumas das questões em torno dele: A solução de trabalho atual em python 3.6+ éimport collections import _collections_abc as cabc import abc class NonStringIterable(metaclass=abc.ABCMeta): __slots__ = () @abc.abstractmethod def __iter__(self): while False: yield None @classmethod def __subclasshook__(cls, c): if cls is NonStringIterable: if issubclass(c, str): return False return cabc._check_methods(c, "__iter__") return NotImplemented
e demonstrado
>>> typs = ['string', iter(''), list(), dict(), tuple(), set()] >>> [isinstance(o, NonStringIterable) for o in typs] [False, True, True, True, True, True]
Se você deseja adicionar
iter('')
às exclusões, por exemplo, modifique a linhaif issubclass(c, str): return False
ser estar
# `str_iterator` is just a shortcut for `type(iter(''))`* if issubclass(c, (str, cabc.str_iterator)): return False
para obter
[False, False, True, True, True, True]
fonte