Tengo una función que toma un argumento que puede ser un solo elemento o un elemento doble:
def iterable(arg)
if #arg is an iterable:
print "yes"
else:
print "no"
así que eso:
>>> iterable (("f", "f")) si >>> iterable (["f", "f"]) si >>> iterable ("ff") No
El problema es que la cadena es técnicamente iterable, por lo que no puedo detectar el ValueError cuando lo intento arg[1]
. No quiero usar isinstance (), porque esa no es una buena práctica (o eso me dijeron).
isinstance
es la forma de hacerlo en lenguajes de tipado dinámico. Algo que no debe usarse todos los días, pero está bien en casos justificados.Respuestas:
Use isinstance (no veo por qué es una mala práctica)
import types if not isinstance(arg, types.StringTypes):
Tenga en cuenta el uso de StringTypes. Asegura que no nos olvidemos de algún tipo oscuro de cadena.
Por el lado positivo, esto también funciona para clases de cadenas derivadas.
class MyString(str): pass isinstance(MyString(" "), types.StringTypes) # true
Además, es posible que desee echar un vistazo a esta pregunta anterior .
Salud.
NB: el comportamiento cambió en Python 3 ya que
StringTypes
ybasestring
ya no están definidos. Dependiendo de sus necesidades, puede reemplazarlosisinstance
porstr
, o una tupla de subconjunto de(str, bytes, unicode)
, por ejemplo, para los usuarios de Cython. Como mencionó @Theron Luhn , también puede usarsix
.fuente
isinstance
podría ser la única forma.do isinstance(arg, str)
. Para obtener una versión compatible con versiones anteriores, considere usar pythonhosted.org/six/#six.string_typestypes.StringTypes
que no está disponible en Python3. ¿Cuál es el valor en Python2?A partir de 2017, aquí hay una solución portátil que funciona con todas las versiones de 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
fuente
Desde Python 2.6, con la introducción de clases base abstractas,
isinstance
(usado en ABC, no en clases concretas) ahora se considera perfectamente aceptable. Específicamente: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 es una copia exacta (cambiando solo el nombre de la clase) de
Iterable
como se define en_abcoll.py
(un detalle de implementación decollections.py
) ... la razón por la que esto funciona como desea, aunquecollections.Iterable
no lo hace, es que este último hace un esfuerzo adicional para garantizar que las cadenas sean considerado iterable, llamandoIterable.register(str)
explícitamente justo después de estaclass
declaración.Por supuesto, es fácil aumentar
__subclasshook__
regresandoFalse
antes de laany
convocatoria para otras clases que desea excluir específicamente de su definición.En cualquier caso, después de haber importado este nuevo módulo como
myiter
,isinstance('ciao', myiter.NonStringIterable)
seráFalse
yisinstance([1,2,3], myiter.NonStringIterable)
seráTrue
, tal como lo solicita, y en Python 2.6 y posteriores, esta se considera la forma adecuada de incorporar tales comprobaciones ... defina una clase base abstracta y comprobarloisinstance
.fuente
isinstance('spam', NonStringIterable)
vuelveTrue
.__iter__
está implementado en cadenas en Python 3. Por lo tanto, mi párrafo "fácil de aumentar" se vuelve aplicable y, por ejemploif issublass(cls, str): return False
, debe agregarse al principio de__subclasshook__
(así como cualquier otra clase que defina__iter__
pero en su la mentalidad no debe aceptarse como "iterables sin cadenas").if issublass(C, str): return False
debería agregarse?Me doy cuenta de que es una publicación antigua, pero pensé que valía la pena agregar mi enfoque para la posteridad de Internet. La función a continuación parece funcionar para mí en la mayoría de las circunstancias con Python 2 y 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
Esto verifica una no cadena iterable por (mis) usando el incorporado
hasattr
que generará unTypeError
cuando su segundo argumento no sea una cadena o una cadena Unicode.fuente
Combinando respuestas anteriores, estoy usando:
import types import collections #[...] if isinstance(var, types.StringTypes ) \ or not isinstance(var, collections.Iterable): #[Do stuff...]
No es 100% a prueba de tontos, pero si un objeto no es iterable, aún puede dejarlo pasar y volver a escribir pato.
Editar: Python3
types.StringTypes == (str, unicode)
. El equivalente de Phython3 es:if isinstance(var, str ) \ or not isinstance(var, collections.Iterable):
fuente
2.x
Habría sugerido:
hasattr(x, '__iter__')
o en vista del comentario de David Charles que modifica esto para Python3, ¿qué pasa con:
hasattr(x, '__iter__') and not isinstance(x, (str, bytes))
3.x
se eliminó el
basestring
tipo de resumen incorporado . Úselo en su lugar. Los tipos y no tienen suficiente funcionalidad en común para garantizar una clase base compartida.str
str
bytes
fuente
__iter__
hay cadenas en Python 3?Como señala correctamente, una sola cadena es una secuencia de caracteres.
Entonces, lo que realmente quieres hacer es averiguar qué tipo de secuencia
arg
es usando isinstance o type (a) == str.Si desea realizar una función que toma una cantidad variable de parámetros, debe hacerlo así:
def function(*args): # args is a tuple for arg in args: do_something(arg)
function ("ff") y function ("ff", "ff") funcionarán.
No puedo ver un escenario en el que se necesite una función isiterable () como la suya. No es isinstance () lo que es de mal estilo, sino situaciones en las que necesita usar isinstance ().
fuente
type(a) == str
se evitará. Es una mala práctica porque no tiene en cuenta tipos similares o tipos derivados destr
.type
no sube en la jerarquía de tipos, mientras queisinstance
sí, por lo tanto, es mejor usarisinstance
.Para expandir explícitamente el excelente truco de Alex Martelli
collections.py
y abordar algunas de las preguntas que lo rodean: la solución de trabajo actual en python 3.6+ esimport 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
y demostrado
>>> typs = ['string', iter(''), list(), dict(), tuple(), set()] >>> [isinstance(o, NonStringIterable) for o in typs] [False, True, True, True, True, True]
Si desea agregar
iter('')
a las exclusiones, por ejemplo, modifique la líneaif 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
Llegar
[False, False, True, True, True, True]
fuente