Pregunta

Esto es lo que hago normalmente con el fin de comprobar que la entrada es una list / tuple - pero no una str. Debido a que muchas veces me encontré con errores donde una función pasa un objeto str por error, y la función objetivo hace for x in lst suponiendo que lst es en realidad un list o tuple.

assert isinstance(lst, (list, tuple))

Mi pregunta es: ¿Hay una mejor manera de lograr esto

¿Fue útil?

Solución

En Python 2 solamente (no Python 3):

assert not isinstance(lst, basestring)

En realidad, es lo que quiere, de lo contrario se perderá en un montón de cosas que actúan como listas, pero no son subclases de list o tuple.

Otros consejos

Recuerde que en Python que queremos usar "duck typing". Por lo tanto, cualquier cosa que actúa como una lista puede ser tratada como una lista. Por lo tanto, no marque para el tipo de una lista, simplemente ver si se comporta como una lista.

Pero cuerdas actúan como una lista demasiado, ya menudo eso no es lo que queremos. Hay momentos en que es aún un problema! Por lo tanto, comprobar explícitamente para una cadena, pero a continuación, utilizar la tipificación de pato.

Esta es una función que escribí para la diversión. Es una versión especial de repr() que imprime cualquier secuencia en corchetes 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

Este es limpio y elegante, en general. Pero lo que es el registro de entrada isinstance() haciendo allí? Eso es una especie de truco. Pero es esencial.

Esta función llama a sí mismo de forma recursiva en cualquier cosa que actúa como una lista. Si no nos ocupamos de la cadena especial, entonces sería tratada como una lista, y se separó un carácter a la vez. Pero entonces la llamada recursiva trataría de tratar a cada personaje como una lista - y que funcionaría! Incluso una cadena de un caracter funciona como una lista! La función mantendría en que se hace llamar recursivamente hasta desbordamiento de pila.

Funciones como ésta, que dependen de cada llamada recursiva descomponer el trabajo a realizar, a tener cadenas de casos especiales - porque no se puede romper una cadena por debajo del nivel de una cadena de un caracter, y incluso una cadena de un caracter actúa como una lista.

Nota: el try / except es la manera más limpia para expresar nuestras intenciones. Pero si este código fuera de algún modo crítico en el tiempo, podríamos queremos reemplazarlo con algún tipo de prueba para ver si es una secuencia arg. En lugar de probar el tipo, probablemente deberíamos probar comportamientos. Si se tiene un método .strip(), es una cadena, por lo que no consideran 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)

EDIT: Originalmente escribí lo anterior con un cheque por __getslice__() pero me di cuenta de que en la documentación del módulo collections, el método interesante es __getitem__(); esto tiene sentido, así es como se indexa un objeto. Eso parece más fundamental que __getslice__() así que cambié los anteriores.

H = "Hello"

if type(H) is list or type(H) is tuple:
    ## Do Something.
else
    ## Do Something.

En Python 3:

import collections.abc

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

cambiado en la versión 3.3: movidas Colecciones clases base abstractas al módulo collections.abc. Para compatibilidad con versiones anteriores, que seguirán siendo visibles en este módulo, así hasta la versión 3.8 en la que dejará de funcionar.

En Python 2:

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"

Python con sabor PHP:

def is_array(var):
    return isinstance(var, (list, tuple))

En términos generales, el hecho de que una función que itera sobre un objeto funciona con cadenas, así como tuplas y las listas es más característica de error. Por supuesto que puede isinstance uso o la tipificación de pato para comprobar una discusión, pero ¿por qué debe usted?

Eso suena como una pregunta retórica, pero no lo es. La respuesta a "¿por qué debería comprobar el tipo del argumento?" probablemente va a sugerir una solución al problema real, no el problema percibido. ¿Por qué es un error cuando una cadena se pasa a la función? También: si se trata de un error cuando una cadena se pasa a esta función, es también un error si algún otro no-list / tupla iterables se pasa a ella? ¿Por qué, o por qué no?

Creo que la respuesta más común a la pregunta es probable que sea que los desarrolladores que escriben f("abc") esperan que la función se comporte como si hubieran f(["abc"]) escritos. Probablemente hay circunstancias en las que tiene más sentido para proteger a los desarrolladores de sí mismos que lo hace para apoyar el caso de uso de iteración a través de los caracteres de una cadena. Pero me gustaría pensar largo y tendido acerca de lo primero.

Probar para facilitar la lectura 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 ayuda.

El objeto str no tiene un atributo __iter__

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

para que pueda hacer una verificació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 sólo funcionará en Python 2.x, 3.x no

Esta no es la intención de responder directamente a la OP, pero quería compartir algunas ideas relacionadas.

Yo estaba muy interesado en @steveha respuesta anterior, que parecía dar un ejemplo en la tipificación de pato parece romper. Pensándolo bien, sin embargo, su ejemplo sugiere que la tipificación de pato es difícil de conformar, pero lo hace no sugieren que str merece ningún tratamiento especial.

Después de todo, un tipo no str (por ejemplo, un tipo definido por el usuario que mantiene algunas estructuras recursivas complicados) pueden causar @steveha función srepr para causar una recursión infinita. Si bien esto es ciertamente bastante improbable, no podemos ignorar esta posibilidad. Por lo tanto, en lugar de str especial-carcasa en srepr, hay que aclarar lo que queremos srepr hay que hacer cuando un resultado infinito de recursividad.

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

Sin embargo, una estructura recursiva muy complicada puede provocar un bucle infinito donde list(arg) == [arg] nunca sucede. Por lo tanto, mientras que la comprobación anterior es útil, no es suficiente. Necesitamos algo así como un límite duro en el nivel de recursividad.

Mi punto es que si va a manejar los tipos de argumentos arbitrarios, la manipulación str a través de la tipificación de pato es mucho, mucho más fácil que la manipulación de los tipos más generales que pueden (en teoría). Así que si usted siente la necesidad de excluir los casos str, en su lugar debe exigir que el argumento es un ejemplo de uno de los pocos tipos que especifique de forma explícita.

Me parece una función tal llamado 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 cumple con sus necesidades.

Lo hago 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 los generadores, creo que se quedan en la próxima 'rendimiento' de ser aprobada en un generador, que puede estropear las cosas aguas abajo. Pero, de nuevo, se trata de una 'unittest'

más simple manera ... 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

De la misma manera "duck typing", ¿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 la materia isinstance / hasattr introspección.

También puede 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. Estoy seguro de que este podría ser indeseable en algunas circunstancias.

Curiosamente, con la asignación "en lugar de" += sin TypeError sería levantado en todo caso si lst es un (no es un tupla ). Es por eso que la asignación se hace de esta manera. Tal vez alguien puede arrojar luz sobre por qué es así.

Sólo hacer esto

if type(lst) in (list, tuple):
    # Do stuff

Python 3 tiene esta:

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 si ambas listas y tuplas, sería:

from typing import List, Tuple

def isit(value):
    return isinstance(value, List) or isinstance(value, Tuple)
assert (type(lst) == list) | (type(lst) == tuple), "Not a valid lst type, cannot be string"

Me tienden a hacer esto (si es que realmente, realmente tenía que):

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top