2010-05-30 20 views
43

En python, ¿hay alguna manera fácil de saber si algo no es una secuencia? Traté de Just Do: if x is not sequence pero Python no les gustaba quePython: compruebe si un objeto es una secuencia

+2

relacionada: En Python, ¿cómo puedo determinar si una variable es Iterable? http://stackoverflow.com/questions/1952464/in-python-how-do-i-determine-if-a-variable-is-iterable/1952481#1952481 – miku

+7

Sí, pero si bien todas las secuencias son iterables, no todos los iterables son secuencias (sets y dicts son contenedores integrados que no son secuencias, por ejemplo). –

Respuesta

57

iter(x) elevará un TypeError si x no se pueden repetir en - pero eso verificación "acepta" conjuntos y diccionarios, a pesar de que "rechaza" otros no-secuencias como None y números.

En las otras manos, cuerdas (que la mayoría de las aplicaciones que desee considerar "elementos individuales" en lugar de secuencias) son en las secuencias de hechos (así, cualquier prueba, a menos que specialcased para cuerdas, se va a confirmar que se encuentran) . Entonces, tales controles simples a menudo no son suficientes.

En Python 2.6 y mejor, se introdujeron las clases base abstractas, y entre otras características potentes ofrecen un soporte más bueno y sistemático para dicha "verificación de categoría".

>>> import collections 
>>> isinstance([], collections.Sequence) 
True 
>>> isinstance((), collections.Sequence) 
True 
>>> isinstance(23, collections.Sequence) 
False 
>>> isinstance('foo', collections.Sequence) 
True 
>>> isinstance({}, collections.Sequence) 
False 
>>> isinstance(set(), collections.Sequence) 
False 

Se habrá dado cuenta cadenas son aún considera "una secuencia" (ya que son ), pero al menos se consiguen dicts y conjuntos fuera del camino. Si desea excluir cuerdas de su concepto de "ser secuencias", podría utilizar collections.MutableSequence (pero que también excluye tuplas, que, como cuerdas, son secuencias, pero no son mutables), o hacerlo de forma explícita:

import collections 

def issequenceforme(obj): 
    if isinstance(obj, basestring): 
     return False 
    return isinstance(obj, collections.Sequence) 

Sazonar al gusto, y servir caliente! -)

+10

Tenga en cuenta que este ejemplo de código arrojará el resultado incorrecto para los objetos que implementan el protocolo de secuencia, pero no involucran el ABC 'collections.Sequence'. –

+4

Sí: a diferencia de un ABC más simple, Sequence no implementa un método de clase '__subclasshook__', por lo que nunca reconocerá automáticamente una clase que eligió no 'registrar' con él (o heredar de él) - sería esencialmente imposible decir por introspección si el '' __getitem__' de una clase acepta enteros y sectores, levanta 'IndexError' en índices incorrectos, etc. - todo lo que necesita para descartar' dict' y 'set', esencialmente (que _do_ parece" implementar la secuencia protocolo "si solo haces introspección ... pero luego no!"). –

+0

Los conjuntos son bastante fáciles de descartar, ya que no tienen '__getitem__', pero las asignaciones son mucho más difíciles. El mejor control que he visto es probablemente buscar 'keys', como' dict.update', pero eso aún deja mucho que desear. – user2357112

4

¿Por qué haces esto? La forma normal aquí es requerir cierto tipo de cosas (una secuencia o un número o un objeto parecido a un archivo, etc.) y luego usarlo sin verificar nada. En Python, normalmente no usamos clases para llevar información semántica, sino que simplemente usamos los métodos definidos (esto se llama "tipa de pato"). También preferimos las API donde sabemos exactamente qué esperar; utilice argumentos de palabra clave, preprocesamiento o defina otra función si desea cambiar el funcionamiento de una función.

+3

Es una restricción de asignación. Si el argumento que se nos pasa no es un int, largo o secuencia, necesitamos generar un 'TypeError' – nicotine

+1

@nicotine, reconocemos que esta asignación indica un diseño que generalmente no es ficticio ni frágil. Considere el caso normal, donde un objeto debería ser exactamente un tipo de cosa. Si se supone que un parámetro es una secuencia, pero obtienes un int, cuando indexas o iteras sobre él ya obtendrás un 'TypeError'. De manera similar, si intentas realizar operaciones de enteros con una secuencia, lo harías. –

+0

Proporcionando una API consistente (cuando sea posible) que espera un solo tipo de cosas le permite escribir un código más simple que aún genera errores cuando algo sale mal pero es más robusto porque admite tipos en los que no pensó pero que satisfacen los verdaderos objetivo (por ejemplo, una clase personalizada que implementa '__len__' y' __getitem__' funcionaría como una secuencia.) –

6

El Python 2.6.5 documentation describe los siguientes tipos de secuencia: cadena, cadena Unicode, lista, tupla, búfer y xrange.

def isSequence(obj): 
    return type(obj) in [str, unicode, list, tuple, buffer, xrange] 
+14

El problema con esta respuesta es que no detectará secuencias que no sean tipos incorporados. Una "secuencia" de Python es cualquier objeto que implementa los métodos necesarios para responder a las operaciones de secuencia. – intuited

+3

Además, use 'isinstance' en lugar de' type' para admitir subclases. – bfontaine

-3

qué preguntarse por qué

intentar conseguir una longitud y si excepción return false

def haslength(seq): 
    try: 
     len(seq) 
    except: 
     return False 
    return True 
+1

Un 'conjunto' tiene una longitud pero no es una secuencia. – bfontaine

4

Como Python "se adhiere" duck typing, uno de los enfoque es comprobar si un objeto tiene alguna miembro (método).

Una secuencia tiene longitud, tiene una secuencia de elementos y admite el corte [doc]. Por lo tanto, sería así:

def is_sequence(obj): 
    t = type(obj) 
    return hasattr(t, '__len__') and hasattr(t, '__getitem__') 
    # additionally: and hasattr(t, '__setitem__') and hasattr(t, '__delitem__') 

Son todos los métodos especiales, __len__() debe devolver número de elementos, __getitem__(i) debería devolver un artículo (en secuencia es i -ésimo, pero no con mapeo), __getitem__(slice(start, stop, step)) debe devolver subsecuencia y __setitem__ y __delitem__ como usted espera.Este es un contrato de este tipo, pero si el objeto realmente hace esto o no depende de si el objeto se adhiere o no al contrato.

Nota que, la función anterior también devolverá True para el mapeo, p. Ej. dict, ya que la asignación también tiene estos métodos. Para superar esto, se puede hacer un más pesado el trabajo:

def is_sequence(obj): 
    try: 
     len(obj) 
     obj[0:0] 
     return True 
    except TypeError: 
     return False 

Pero mayor parte del tiempo que no necesita esto, simplemente hacer lo que quiera como si el objeto es una secuencia y capturar una excepción si lo desea. Esto es más pitónico.

+0

El punto importante acerca de dict es que si lo trata como una secuencia, obtendrá las claves, no los valores, y la información se perderá. –

+2

Si usa 'hasattr()', necesita verificar el tipo del objeto para los métodos mágicos, no el objeto en sí. Consulte [Python 2] (https://docs.python.org/2/reference/datamodel.html#special-method-lookup-for-new-style-classes) y [Python 3] (https: // docs .python.org/3/reference/datamodel.html # special-method-searchup) documentación sobre cómo se buscan los métodos especiales. – augurar

+0

@augurar Thanks man – BornToCode

4

creo que el siguiente fragmento de código hace lo que quiere:

def is_sequence(obj): 
    return hasattr(type(obj), '__iter__') 
+2

Debería ser 'hasattr (type (obj), '__iter __')', ver [este comentario] (https://stackoverflow.com/questions/2937114/#comment71833903_31043360). – augurar

Cuestiones relacionadas