Esto es lo que normalmente hago para determinar que la entrada es a list
/ tuple
- pero no a str
. Porque muchas veces me topé con errores en los que una función pasa un str
objeto por error, y la función de destino for x in lst
supone que en lst
realidad es un list
o tuple
.
assert isinstance(lst, (list, tuple))
Mi pregunta es: ¿hay una mejor manera de lograr esto?
Respuestas:
Solo en Python 2 (no Python 3):
En realidad es lo que quieres, de lo contrario te perderás muchas cosas que actúan como listas, pero no son subclases de
list
otuple
.fuente
basestring
desapareció, y solo lo verificasisinstance(lst, str)
.set
, expresiones generadoras, iteradores. Hay cosas exóticas, cosasmmap
menos exóticasarray
que actúan más o menos como listas, y probablemente muchas más que he olvidado.lst
sea iterable, mientras que el original sí (por ejemplo, un int pasaría esta verificación)assert isinstance(lst, (list, tuple)) and assert not isinstance(lst, basestring)
Recuerde que en Python queremos usar "escribir pato". Entonces, cualquier cosa que actúe como una lista puede tratarse como una lista. Por lo tanto, no verifique el tipo de lista, solo vea si actúa como una lista.
Pero las cadenas también actúan como una lista, y a menudo eso no es lo que queremos. ¡Hay momentos en que incluso es un problema! Por lo tanto, verifique explícitamente una cadena, pero luego use la escritura de pato.
Aquí hay una función que escribí por diversión. Es una versión especial de
repr()
que imprime cualquier secuencia entre paréntesis angulares ('<', '>').Esto es limpio y elegante, en general. ¿Pero qué hace ese
isinstance()
cheque allí? Eso es una especie de truco. Pero es esencial.Esta función se llama recursivamente sobre cualquier cosa que actúe como una lista. Si no manejáramos la cadena especialmente, entonces sería tratada como una lista y dividiría un carácter a la vez. Pero entonces la llamada recursiva trataría de tratar a cada personaje como una lista, ¡y funcionaría! ¡Incluso una cadena de un carácter funciona como una lista! La función seguiría llamándose recursivamente hasta que se desborde la pila.
Las funciones como esta, que dependen de cada llamada recursiva que desglosa el trabajo a realizar, tienen que ser cadenas de casos especiales, porque no puede dividir una cadena por debajo del nivel de una cadena de un carácter, e incluso una La cadena de caracteres actúa como una lista.
Nota: la
try
/except
es la forma más limpia de expresar nuestras intenciones. Pero si este código fuera de alguna manera crítico en el tiempo, podríamos reemplazarlo con algún tipo de prueba para ver siarg
es una secuencia. En lugar de probar el tipo, probablemente deberíamos probar los comportamientos. Si tiene un.strip()
método, es una cadena, así que no lo considere una secuencia; de lo contrario, si es indexable o iterable, es una secuencia:EDITAR: Originalmente escribí lo anterior con un cheque
__getslice__()
pero noté que en lacollections
documentación del módulo, el método interesante es__getitem__()
; esto tiene sentido, así es como indizas un objeto. Eso parece más fundamental que__getslice__()
eso, cambié lo anterior.fuente
srepr
es una idea muy interesante. Pero tengo una opinión diferente a la tuya sobre si necesita un caso especialstr
. Sí,str
es con mucho el iterativo más obvio y común que causaría una recursión infinita ensrepr
. Pero puedo imaginar fácilmente iterables definidos por el usuario que se comporten de la misma manera (con o sin una buena razón). En lugar de un caso especialstr
, debemos admitir que este enfoque puede encontrarse con una recursión infinita, y aceptar alguna forma de tratarlo. Publicaré mi sugerencia en una respuesta.srepr()
está bien como está. Necesitamos una función recursiva para manejar cosas como una lista anidada dentro de otra lista; pero para las cadenas preferimos que se impriman como"foo"
antes<'f', 'o', 'o'>
. Entonces, una comprobación explícita de una cadena tiene sentido aquí. Además, realmente no hay otros ejemplos de tipos de datos en los que la iteración siempre devuelva un iterable y la recursión siempre cause un desbordamiento de la pila, por lo que no necesitamos una propiedad especial para probar esto ("La practicidad supera la pureza").__iter__()
método en Python 3, pero no en Python 2. Le faltan paréntesisis_sequence()
, debería leer:return (not hasattr(arg, "strip") and (hasattr(arg, "__getitem__") or hasattr(arg, "__iter__")))
fuente
if isinstance( H, (list, tuple) ): ...
es más corta y clara.if type(H) in [list, tuple]:
Para Python 3:
Para Python 2:
fuente
collections.Sequence
pero lo probé y veo que sí. También lo hacexrange
. Aún mejor, esta prueba excluyedict
, que tiene ambos__getitem__
y__iter__
.inspect.getmro(list)
no incluyeSequence
? ¿Cómo se supone que debemos averiguar qué podemos hacerisinstance
cuandogetmro
no se muestra todo?Sequence
Es una clase abstracta.Python con sabor PHP:
fuente
__getitem__
. Además, el nombre es engañoso, ya que también hay un módulo de matriz. Y la var también podría ser un numpy.ndarray o cualquier otro tipo, que tiene__getitem__
. Consulte stackoverflow.com/a/1835259/470560 para obtener la respuesta correcta.str
también tiene__getitem__
por lo tanto su cheque no excluyestr
__getitem__
es un mal consejo aquí.En términos generales, el hecho de que una función que itera sobre un objeto funciona tanto en cadenas como en tuplas y listas es más característica que error. Ciertamente puede usar
isinstance
o esquivar la escritura para verificar un argumento, pero ¿por qué debería hacerlo?Eso suena como una pregunta retórica, pero no lo es. La respuesta a "¿por qué debería verificar el tipo de argumento?" probablemente va a sugerir una solución al problema real, no el problema percibido. ¿Por qué es un error cuando se pasa una cadena a la función? Además: si se trata de un error cuando se pasa una cadena a esta función, ¿también es un error si se le pasa algún otro iterable que no sea de lista / tupla? ¿Por qué o por qué no?
Creo que la respuesta más común a la pregunta es que los desarrolladores que escriben
f("abc")
esperan que la función se comporte como si hubiera escritof(["abc"])
. Probablemente hay circunstancias en las que tiene más sentido proteger a los desarrolladores de sí mismos que respaldar el caso de uso de iterar a través de los caracteres en una cadena. Pero pensaría mucho en ello primero.fuente
Pruebe esto para facilitar la lectura y las mejores prácticas:
Python2
Python3
Espero eso ayude.
fuente
AttributeError: module 'types' has no attribute 'ListType'
from typing import List
->isinstance([1, 2, 3], List
=True
, yisinstance("asd", List)
=False
El
str
objeto no tiene un__iter__
atributo.para que puedas hacer un chequeo
y esto también generará un
AssertionError
beneficio para cualquier otro objeto no iterable.Editar: como Tim menciona en los comentarios, esto solo funcionará en python 2.x, no 3.x
fuente
hasattr('','__iter__')
vuelveTrue
. Y, por supuesto, eso tiene sentido ya que puede iterar sobre una cadena.Esto no tiene la intención de responder directamente al OP, pero quería compartir algunas ideas relacionadas.
Estaba muy interesado en la respuesta de @steveha anterior, que parecía dar un ejemplo donde la escritura de patos parece romperse. Pensándolo bien, sin embargo, su ejemplo sugiere que es difícil cumplir con la escritura del pato, pero lo hace no sugiere que
str
merezca un manejo especial.Después de todo, un no
str
tipo (por ejemplo, un tipo definido por el usuario que mantiene algunas estructuras recursivas complicadas) puede causar que lasrepr
función @steveha provoque una recursión infinita. Si bien esto es ciertamente poco probable, no podemos ignorar esta posibilidad. Por lo tanto, en lugar de especial-carcasastr
ensrepr
, hay que aclarar lo que queremossrepr
hacer cuando un infinito resultados de recursividad.Puede parecer que un enfoque razonable es simplemente romper la recursión en
srepr
el momentolist(arg) == [arg]
. Esto, de hecho, resolvería completamente el problema constr
, sin ningunaisinstance
.Sin embargo, una estructura recursiva realmente complicada puede causar un bucle infinito donde
list(arg) == [arg]
nunca sucede. Por lo tanto, si bien la comprobación anterior es útil, no es suficiente. Necesitamos algo así como un límite estricto en la profundidad de recursión.Mi punto es que si planea manejar tipos de argumentos arbitrarios, el manejo
str
mediante la escritura de pato es mucho, mucho más fácil que el manejo de los tipos más generales que puede encontrar (en teoría). Entonces, si siente la necesidad de excluirstr
instancias, debería exigir que el argumento sea una instancia de uno de los pocos tipos que especifique explícitamente.fuente
str
que maneja el código de caso especial. Pero tal vez debería haber una nueva propiedad estándar que el código pueda inspeccionar,.__atomic__
digamos, que indique que algo no puede desglosarse más. Probablemente sea demasiado tarde para agregar otra función incorporadaatomic()
a Python, pero tal vez podamos agregarfrom collections import atomic
o algo.Encuentro una función llamada is_sequence en tensorflow .
Y he verificado que satisface tus necesidades.
fuente
Hago esto en mis casos de prueba.
No probado en los generadores, creo que te quedan en el próximo 'rendimiento' si se pasa en un generador, que puede arruinar las cosas aguas abajo. Pero, de nuevo, esta es una 'prueba de unidad'
fuente
En la manera de "escribir pato", ¿qué tal
o
respectivamente. Esto evita las cosas
isinstance
/hasattr
introspección.También puede verificar viceversa:
Todas las variantes en realidad no cambian el contenido de la variable, sino que implican una reasignación. No estoy seguro de si esto podría ser indeseable en algunas circunstancias.
Curiosamente, con la asignación "en el lugar"
+=
noTypeError
se plantearía en ningún caso si selst
trata de una lista (no una tupla ). Es por eso que la tarea se realiza de esta manera. Tal vez alguien pueda arrojar luz sobre por qué es eso.fuente
forma más simple ... usando
any
yisinstance
fuente
Otra versión de tipeo de pato para ayudar a distinguir los objetos con forma de cadena de otros objetos con secuencia.
La representación de cadena de los objetos en forma de cadena es la cadena misma, por lo que puede verificar si obtiene un objeto igual del
str
constructor:Esto debería funcionar para todos los objetos compatibles con
str
y para todo tipo de objetos iterables.fuente
Python 3 tiene esto:
Entonces, para verificar tanto las Listas como las Tuplas, sería:
fuente
fuente
Solo haz esto
fuente
en python> 3.6
fuente
str
, no una cadena. Intentaisinstance('my_string', collections.abc.Container)
y verás que volveráTrue
. Esto se debe a queabc.Container
proporciona el__contains__
método, y las cadenas lo tienen, por supuesto.Tiendo a hacer esto (si realmente, realmente tuviera que hacerlo):
fuente
some_var
una instancia de una clase que es una subclase delist()
? Su código no tendrá idea de qué hacer con él, aunque funcionará perfectamente bajo el código "hacer algo con una lista". Y rara vez necesita preocuparse por la diferencia entre una lista y una tupla. Lo siento, -1.type(tuple())
,tuple
lo haré. Lo mismo para la lista. Además, ambosstr
yunicode
extenderbasestring
, que es el tipo de cadena real, por lo que desea verificar eso en su lugar.type(i) is list
. Además,type(list())
es solo enlist
sí mismo ... Finalmente, esto no funciona correctamente con las subclases. Sii
es de hecho y OrderedDict, o algún tipo de nombre de tupla, este código tratará como una cadena.