2008-11-03 26 views
11

Me ha picado algo inesperado recientemente. Yo quería hacer algo así:¿Por qué son repetibles las iteraciones?

try : 
    thing.merge(iterable) # this is an iterable so I add it to the list 
except TypeError : 
    thing.append(iterable) # this is not iterable, so I add it 

Bueno, estaba trabajando bien hasta que pasé un objeto que hereda de Excepción que se supone que debe ser añadido.

Desafortunadamente, una excepción es iterable. El siguiente código no plantea ningún TypeError:

for x in Exception() : 
    print 1 

¿Alguien sabe por qué?

Respuesta

11

Tenga en cuenta que lo que está sucediendo no está relacionado con ningún tipo de conversión de cadena implícita, etc. sino porque la clase Exception implementa __getitem __() y la usa para devolver los valores en la tupla de args (ex.args). Puedes ver esto por el hecho de que obtienes toda la cadena como tu primer y único elemento en la iteración, en lugar del resultado de carácter por carácter que obtendrías si iteraras sobre la cadena.

Esto también me sorprendió, pero pensando en ello, supongo que es por razones de compatibilidad hacia atrás. Python solía (pre-1.5) carecer de la jerarquía de excepciones de clase actual. En su lugar, se lanzaron cadenas, con (normalmente) un argumento de tupla para cualquier detalle que deba pasarse al bloque de manejo. es decir:

try: 
    raise "something failed", (42, "some other details") 
except "something failed", args: 
    errCode, msg = args 
    print "something failed. error code %d: %s" % (errCode, msg) 

Parece que este comportamiento se puso en para evitar la ruptura de pre-1,5 código esperando una tupla de argumentos, en lugar de un objeto de excepción no iterable. Hay un par de ejemplos de esto con IOError en la sección de Rotura fatal de las anteriores link

Las excepciones de cadena se han deducido por un tiempo y se van a ir a Python 3. Ahora he comprobado cómo Python 3 maneja la excepción objetos, y parece que ya no están allí iterables:

>>> list(Exception("test")) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: 'Exception' object is not iterable 

[Editar] comportamiento de python3 cuadros

+0

Brillant! Thx por la explicación. Me gustaría aceptar la respuesta, pero parece que necesitamos un poco más de votación antes de que se me permita. –

3

NO ES VÁLIDO. Revisa a Brian con el guante.

Ok, yo sólo lo consiguió:

for x in Exception("test") : 
    print x 
    ....:  
    ....:  
test 

No se moleste ;-)

De todos modos, es bueno saber.

EDIT: mirando a los comentarios, tengo ganas de agregar algunas explicaciones.

Una excepción contiene un mensaje que ha pasado a durante la instanciación:

raise Exception("test") 

Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
Exception: test 

Es justo decir que el mensaje es lo que define la excepción el mejor, por lo str() devuelve:

print Exception("test") 
test 

Ahora, sucede que las Excepciones se convierten implícitamente en cadena cuando se usan en algo distinto de un contexto de excepción.

Así que cuando lo haga:

for x in Exception("test") : 
    print x 

estoy interactuando sobre la "prueba" cadena.

Y cuando lo haga:

for x in Exception() : 
    print x 

Yo iterar sobre una cadena vacía. Difícil. Porque cuando se trata de mi problema:

try : 
    thing.merge(ExceptionLikeObject) 
except TypeError : 
    ... 

Esto no va a aumentar nada desde ExceptionLikeObject se considera como una cadena.

Bien, ahora sabemos el CÓMO, pero aún no el POR QUÉ. ¿Tal vez la excepción incorporada hereda de la cadena incorporada? Porque por lo que yo sé:

  • añadiendo str no hace que cualquier objeto iterable.
  • Pasé por alto el problema al sobrecargar iter, lo que aumenta TypeError!

Ya no es un problema, pero sigue siendo un misterio.

+0

en realidad no se trata como una cadena, que está tratando como la tupla argumento. es decir, lista (Excepción ("prueba")) == ["prueba"], no ["t", "e", "s", "t"]. Del mismo modo lista (Excepción (1,2)) == [1, 2]. Ver mi respuesta a continuación para algunas especulaciones sobre por qué esto puede ser. – Brian

2

En realidad, todavía no lo entiendo.Veo que iterar una Excepción te da los argumentos originales a la excepción, no estoy seguro de por qué alguien querría eso. La iteración implícita es, creo, una de las pocas trampas en Python que todavía me deja boquiabierto.

+0

No es una iteración implícita lo que causa esto, es una conversión implícita. La excepción se convierte en una cadena de forma automática, que es iterable. –

+0

Motivo votado porque creo que es injusto no votar porque no entiendes algo. Añadiré más explicaciones en mi respuesta. –

+0

@Jason: No, la excepción es en realidad actuar como una lista de parámetros (implementa __getitem__). es decir, list (ex) es equivalente a list (ex.args). También me resulta extraño. Sospecho que actúa así por razones de compatibilidad hacia atrás con antiguas excepciones de string + tuple style. – Brian

Cuestiones relacionadas