¿Cómo comprobar si un objeto es una lista o tupla (pero no una cadena)?


Esto es lo que normalmente hago para determinar que la entrada es una list/tuple - pero no un str. Porque muchas veces me topé con errores donde una función pasa un objeto str por error, y la función de destino lo hace for x in lst asumiendo que lst es en realidad un list o tuple.

assert isinstance(lst, (list, tuple))

Mi pregunta es: ¿hay una mejor manera de lograr esto?

Author: kenorb, 2009-12-02

17 answers

Solo en python 2 (no python 3):

assert not isinstance(lst, basestring)

Es realmente lo que quieres, de lo contrario te perderás muchas cosas que actúan como listas, pero no son subclases de list o tuple.

 309
Author: Nick Craig-Wood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-16 14:21:27

Recuerde que en Python queremos usar "duck typing". Por lo tanto, cualquier cosa que actúe como una lista puede ser tratada 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 si hay una cadena, pero luego use duck typing.

Aquí hay una función que escribí por diversión. Es una versión especial de repr() que imprime cualquier secuencia en soportes angulares('').

def srepr(arg):
    if isinstance(arg, basestring): # Python 3: isinstance(arg, str)
        return repr(arg)
    try:
        return '<' + ", ".join(srepr(x) for x in arg) + '>'
    except TypeError: # catch when for loop fails
        return repr(arg) # not a sequence so just return repr

Esto es limpio y elegante, en general. Pero, ¿qué está haciendo ese isinstance() cheque allí? Eso es una especie de truco. Pero es esencial.

Esta función se llama recursivamente a cualquier cosa que actúe como una lista. Si no manejamos la cadena especialmente, entonces se trataría 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 un solo carácter string funciona como una lista! La función seguiría llamándose recursivamente hasta el desbordamiento de la pila.

Las funciones como esta, que dependen de cada llamada recursiva desglosando el trabajo a realizar, tienen cadenas de caracteres especiales because porque no se puede desglosar una cadena por debajo del nivel de una cadena de un carácter, e incluso una cadena de un carácter actúa como una lista.

Nota: el 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, queremos reemplazarlo con algún tipo de prueba para ver si arg es una secuencia. En lugar de probar el tipo, probablemente deberíamos probar los comportamientos. Si tiene un método .strip(), es una cadena, así que no lo consideres una secuencia; de lo contrario, si es indexable o iterable, es una secuencia:

def is_sequence(arg):
    return (not hasattr(arg, "strip") and
            hasattr(arg, "__getitem__") or
            hasattr(arg, "__iter__"))

def srepr(arg):
    if is_sequence(arg):
        return '<' + ", ".join(srepr(x) for x in arg) + '>'
    return repr(arg)

EDITAR: Originalmente escribí lo anterior con una verificación para __getslice__() pero noté que en la documentación del módulo collections, el método interesante es __getitem__(); esto tiene sentido, así es como indexas un objeto. Eso parece más fundamental que __getslice__() así que cambié lo anterior.

 162
Author: steveha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-07 06:25:56
H = "Hello"

if type(H) is list or type(H) is tuple:
    ## Do Something.
else
    ## Do Something.
 107
Author: Mani Shankar Venkankatachalam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-27 10:57:58
import collections

if isinstance(obj, collections.Sequence) and not isinstance(obj, basestring):
    print "obj is a sequence (list, tuple, etc) but not a string or unicode"

Para Python 3, nota por el docs:

Cambiado en la versión 3.3: Se movieron las Clases Base Abstractas de Colecciones a las colecciones.módulo abc. Para la compatibilidad hacia atrás, siguen siendo visibles en este módulo.

 51
Author: suzanshakya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-24 01:36:26

Python con sabor PHP:

def is_array(var):
    return isinstance(var, (list, tuple))
 32
Author: Cesar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-11 23:30:04

En términos generales, el hecho de que una función que itera sobre un objeto funcione en cadenas, así como en tuplas y listas es más característica que error. Ciertamente puede usar isinstance o duck typing 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 al problema percibido. ¿Por qué es un error cuando una cadena es pasa a la función? También: si es un error cuando se pasa una cadena a esta función, ¿también es un error si se le pasa algún otro iterable no-lista/tupla? ¿Por qué o por qué no?

Creo que la respuesta más común a la pregunta probablemente sea que los desarrolladores que escriben f("abc") esperan que la función se comporte como si hubieran escrito f(["abc"]). Probablemente hay circunstancias en las que tiene más sentido proteger a los desarrolladores de sí mismos que apoyar el caso de uso de iterar a través de los caracteres en una cadena. Pero lo pensaría mucho antes.

 9
Author: Robert Rossney,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-02 20:33:02

El objeto str no tiene un atributo __iter__

>>> hasattr('', '__iter__')
False 

Para que pueda hacer una comprobación

assert hasattr(x, '__iter__')

Y esto también levantará un buen AssertionError para cualquier otro objeto no iterable también.

Editar: Como Tim menciona en los comentarios, esto solo funcionará en python 2.x, no 3.x

 6
Author: Moe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-02 21:09:51

Pruebe esto para la legibilidad y las mejores prácticas:

Python2

import types
if isinstance(lst, types.ListType) or isinstance(lst, types.TupleType):
    # Do something

Python3

import typing
if isinstance(lst, typing.List) or isinstance(lst, typing.Tuple):
    # Do something

Espero que ayude.

 5
Author: Om Prakash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-04 04:43:44

Esto no pretende 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 pato parece romperse. Pensándolo bien, sin embargo, su ejemplo sugiere que el duck typing es difícil de cumplir, pero no sugiere que str merece un manejo especial.

Después de todo, un tipo no - str (por ejemplo, un tipo definido por el usuario que mantiene algunos estructuras recursivas) puede causar que la función @ steveha srepr cause una recursión infinita. Si bien es cierto que esto es bastante improbable, no podemos ignorar esta posibilidad. Por lo tanto, en lugar de la carcasa especial str en srepr, debemos aclarar lo que queremos srepr hacer cuando resulta una recursión infinita.

Puede parecer que un enfoque razonable es simplemente romper la recursión en srepr el momento list(arg) == [arg]. Esto, de hecho, resolvería completamente el problema con str, sin ninguna isinstance.

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 duro en la profundidad de recursión.

Mi punto es que si planea manejar tipos de argumentos arbitrarios, manejar str a través de duck typing es mucho, mucho más fácil que manejar los tipos más generales que puede encontrar (teóricamente). Así que si usted siente la necesidad de excluir str instancias, en su lugar, debe exigir que el argumento sea una instancia de uno de los pocos tipos que especifique explícitamente.

 4
Author: max,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-29 07:42:20

Encuentro una función llamada is_sequence en tensorflow.

def is_sequence(seq):
  """Returns a true if its input is a collections.Sequence (except strings).
  Args:
    seq: an input sequence.
  Returns:
    True if the sequence is a not a string and is a collections.Sequence.
  """
  return (isinstance(seq, collections.Sequence)
and not isinstance(seq, six.string_types))

Y he verificado que satisface sus necesidades.

 3
Author: lerner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-19 09:47:57

Hago esto en mis casos de prueba.

def assertIsIterable(self, item):
    #add types here you don't want to mistake as iterables
    if isinstance(item, basestring): 
        raise AssertionError("type %s is not iterable" % type(item))

    #Fake an iteration.
    try:
        for x in item:
            break;
    except TypeError:
        raise AssertionError("type %s is not iterable" % type(item))

No probado en generadores, creo que se queda en el siguiente 'rendimiento' si se pasa en un generador, lo que puede arruinar las cosas aguas abajo. Pero de nuevo, esto es un'unittest'

 2
Author: FlipMcF,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-02 00:25:26

La forma más sencilla... usando any y isinstance

>>> console_routers = 'x'
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
False
>>>
>>> console_routers = ('x',)
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
True
>>> console_routers = list('x',)
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
True
 1
Author: abarik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-01 09:27:19

En la manera de "escribir pato", ¿qué tal

try:
    lst = lst + []
except TypeError:
    #it's not a list

O

try:
    lst = lst + ()
except TypeError:
    #it's not a tuple

, Respectivamente. Esto evita el isinstance / hasattr cosas de introspección.

También puedes comprobar viceversa:

try:
    lst = lst + ''
except TypeError:
    #it's not (base)string

Todas las variantes en realidad no cambian el contenido de la variable, pero implican una reasignación. No estoy seguro de si esto podría ser indeseable bajo algunas circunstancias.

Curiosamente, con la asignación" en lugar " += no TypeError se plantearía en cualquier caso si lst es un list (no una tupla ). Es por eso que la tarea se hace de esta manera. Tal vez alguien pueda arrojar luz sobre por qué es eso.

 1
Author: utobi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-18 14:25:33

Python 3 tiene esto:

from typing import List

def isit(value):
    return isinstance(value, List)

isit([1, 2, 3])  # True
isit("test")  # False
isit({"Hello": "Mars"})  # False
isit((1, 2))  # False

Así que para comprobar tanto las Listas como las Tuplas, sería:

from typing import List, Tuple

def isit(value):
    return isinstance(value, List) or isinstance(value, Tuple)
 0
Author: Juha Untinen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-03 20:31:37

Solo haz esto

if type(lst) in (list, tuple):
    # Do stuff
 -1
Author: ATOzTOA,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-12 19:07:49

Si ya tienes pandas disponibles puedes hacer esto:

variable = pd.Series(variable).tolist()

Esto es lo que hago para asegurar una lista.

 -4
Author: Ivelin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-06 17:41:12

Tiendo a hacer esto (si realmente, realmente tenía que hacerlo):

for i in some_var:
   if type(i) == type(list()):
       #do something with a list
   elif type(i) == type(tuple()):
       #do something with a tuple
   elif type(i) == type(str()):
       #here's your string
 -5
Author: DrBloodmoney,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-02 19:59:42