¿Por qué Python usa 'else' después de los bucles for y while?


482

Entiendo cómo funciona esta construcción:

for i in range(10):
    print(i)

    if i == 9:
        print("Too big - I'm giving up!")
        break;
else:
    print("Completed successfully")

Pero no entiendo por qué elsese usa aquí como palabra clave, ya que sugiere que el código en cuestión solo se ejecuta si el forbloque no se completa, ¡lo que es lo contrario de lo que hace! No importa cómo lo piense, mi cerebro no puede avanzar sin problemas desde la fordeclaración hasta el elsebloqueo. Para mí, continueo continuewithtendría más sentido (y estoy tratando de entrenarme para leerlo como tal).

Me pregunto cómo los codificadores de Python leen esta construcción en su cabeza (o en voz alta, si lo desea). ¿Quizás me estoy perdiendo algo que haría que tales bloques de código sean más fácilmente descifrables?


26
Es posible que desee traducirlo a "entonces" en su cabeza.
Marcin

63
No olvide la línea clave en el Zen de Python: "... esa manera puede no ser obvia al principio a menos que sea holandés".
Daniel Roseman

51
En mi cabeza lo traduzco a "si no se rompe" . Y, dado que breakse usa mucho en los bucles "Lo he encontrado" , puede traducirlo a "si no lo encuentra" , que no está lejos de lo que se elselee
MestreLion

29
Creo que la verdadera pregunta que mucha gente tiene aquí es "¿Cuál es la diferencia entre for ... else foo()y simplemente poner foo()después del ciclo for?" Y la respuesta es que se comportan de manera diferente solo si el ciclo contiene un break(como se describe en detalle a continuación).
Sam Kauffman

10
Un punto y coma en pitón ... me duelen los ojos ... aunque es sintácticamente correcto, no es una buena práctica hacerlo
DarkCygnus

Respuestas:


278

Es una construcción extraña incluso para codificadores experimentados de Python. Cuando se usa junto con for-loops, básicamente significa "encontrar algún elemento en el iterable, de lo contrario, si no se encontró ninguno, haga ...". Como en:

found_obj = None
for obj in objects:
    if obj.key == search_key:
        found_obj = obj
        break
else:
    print('No object found.')

Pero cada vez que vea esta construcción, una mejor alternativa es encapsular la búsqueda en una función:

def find_obj(search_key):
    for obj in objects:
        if obj.key == search_key:
            return obj

O use una lista de comprensión:

matching_objs = [o for o in objects if o.key == search_key]
if matching_objs:
    print('Found {}'.format(matching_objs[0]))
else:
    print('No object found.')

No es semánticamente equivalente a las otras dos versiones, pero funciona lo suficientemente bien en el código crítico que no es de rendimiento donde no importa si itera la lista completa o no. Otros pueden estar en desacuerdo, pero yo personalmente evitaría usar los bloques for-else o while-else en el código de producción.

Ver también [Python-ideas] Resumen de para ... otros hilos


50
La comprensión de la lista es incorrecta. Si está buscando un solo elemento, como en los forejemplos de bucle, y desea usar una expresión de generador / comprensión de lista, entonces quiere next((o for o in objects if o.key == search_key), None)o envolverlo en un try/ excepty no usar ningún valor predeterminado en lugar de un if/ else.
agf

44
y al igual que la respuesta de Lance Helsten, hay casos reales en los que es mejor usar una for/elseconstrucción.
andrean

55
Salud. Tenía un archivo con sangría grave donde elsese emparejó con un fory no tenía idea de que era legal.
maxywb

3
Creo que el bucle for es el más obvio de los constructos allí.
Miles Rout

14
Vale la pena mencionar que la cláusula else se ejecutará incluso si el bucle for tiene valores a menos que una breakinstrucción se ejecute explícitamente como en este ejemplo. De los documentos anteriores: "La elsecláusula tiene otro problema percibido: si no hay ninguna breaken el bucle, la elsecláusula es funcionalmente redundante". por ejemplofor x in [1, 2, 3]:\n print x\n else:\n print 'this executes due to no break'
dhackner el

587

Una construcción común es ejecutar un bucle hasta que se encuentre algo y luego salir del bucle. El problema es que si salgo del ciclo o el ciclo termina, necesito determinar qué caso sucedió. Un método es crear un indicador o almacenar una variable que me permita hacer una segunda prueba para ver cómo se salió del bucle.

Por ejemplo, suponga que necesito buscar en una lista y procesar cada elemento hasta encontrar un elemento de marca y luego detener el procesamiento. Si falta el elemento de la bandera, se debe generar una excepción.

Usando el Python for... elseconstruye que tienes

for i in mylist:
    if i == theflag:
        break
    process(i)
else:
    raise ValueError("List argument missing terminal flag.")

Compare esto con un método que no utiliza este azúcar sintáctico:

flagfound = False
for i in mylist:
    if i == theflag:
        flagfound = True
        break
    process(i)

if not flagfound:
    raise ValueError("List argument missing terminal flag.")

En el primer caso, el raiseestá estrechamente unido al bucle for con el que trabaja. En el segundo, el enlace no es tan fuerte y pueden introducirse errores durante el mantenimiento.


69
¡Esto lo explica mejor que la respuesta elegida en la que el autor realmente no entiende de qué se trata!
erikbwork

17
Debo decir que este azúcar sintáctico podría pudrir los dientes de tu proyecto. Esto no haría un Python: the good partslibro.
Boatcoder

1
¿Puede confirmar que, en su ejemplo, process(i)sucede para cada artículo mylistestrictamente antes theflagy no para theflagsí mismo? ¿Es lo que se pretendía?
bli

44
processse ejecutará en cada uno ique exista en la lista antes de que theflagse alcance, no se ejecutará en elementos de la lista después theflagy no se ejecutará en theflag.
Lance Helsten

1
la instrucción else también se ejecuta si el iterable no tiene elementos
Lost Crotchet

173

Hay una excelente presentación de Raymond Hettinger, titulada Transformando el código en una hermosa pitón idiomática , en la que aborda brevemente la historia de la for ... elseconstrucción. La sección relevante es "Distinguir múltiples puntos de salida en bucles" a partir de las 15:50 y continuar durante unos tres minutos. Aquí están los puntos altos:

  • El for ... elseconstructo fue ideado por Donald Knuth como un reemplazo para ciertos GOTOcasos de uso;
  • Reutilizar la elsepalabra clave tenía sentido porque "es lo que Knuth usó, y la gente sabía, en ese momento, que todas [las fordeclaraciones] habían incrustado ify GOTOdebajo, y esperaban que else";
  • En retrospectiva, debería haberse llamado "no break" (o posiblemente "nobreak"), y entonces no sería confuso. *

Entonces, si la pregunta es, "¿Por qué no cambian esta palabra clave?" entonces Cat Plus Plus probablemente dio la respuesta más precisa : en este punto, sería demasiado destructivo para el código existente para ser práctico. Pero si la pregunta que realmente hace es por qué elsese reutilizó en primer lugar, bueno, aparentemente parecía una buena idea en ese momento.

Personalmente, me gusta el compromiso de comentar # no breaken línea donde sea elseque pueda confundirse, de un vistazo, como perteneciente al círculo. Es razonablemente claro y conciso. Esta opción recibe una breve mención en el resumen que Bjorn vincula al final de su respuesta:

Para completar, debo mencionar que con un ligero cambio en la sintaxis, los programadores que desean esta sintaxis pueden tenerla ahora mismo:

for item in sequence:
    process(item)
else:  # no break
    suite

* Cita extra de esa parte del video: "Al igual que si llamamos a la función lambda makefunction, nadie preguntaría, '¿Qué hace lambda?'"


33

Porque no querían introducir una nueva palabra clave en el idioma. Cada uno roba un identificador y causa problemas de compatibilidad con versiones anteriores, por lo que generalmente es el último recurso.


2
Parece finallyque hubiera sido una mejor opción en ese caso. ¿La palabra clave finalmente aún no estaba presente en el momento en que se introdujo esta construcción?
Ponkadoodle

26
@Wallacoloo finallyno es mucho mejor, porque implica que el bloque siempre se ejecutará después del ciclo, y no lo es (porque eso sería redundante con solo poner el código para ejecutarlo después del ciclo).
Cat Plus Plus

Tampoco puede ser finallyporque la cláusula else se ejecuta también cuando continuese usa en el bucle for, eso es posiblemente muchas veces y no solo al final.
pepr

66
La elseejecución de la cláusula @pepr no se ve afectada por continue( documentos y código de prueba )
emitirá

@AirThomas: +1. Tienes razón. El elsese ejecuta solo cuando continuefue el de la última iteración.
pepr

33

Para hacerlo simple, puedes pensarlo así;

  • Si encuentra el breakcomando en el forbucle, elseno se llamará a la parte.
  • Si no encuentra el breakcomando en el forbucle, elsese llamará a la parte.

En otras palabras, si la iteración for loop no se "rompe" con break, la elseparte se llamará.


El elsebloque tampoco se ejecutará si el cuerpo del bucle genera una excepción.
Amal K

17

La forma más fácil que encontré de 'obtener' lo que hizo el for / else, y lo más importante, cuándo usarlo, fue concentrarme en dónde salta la declaración de ruptura. La construcción For / else es un bloque único. La ruptura salta fuera del bloque, y así salta 'sobre' la cláusula else. Si el contenido de la cláusula else simplemente siguiera a la cláusula for, nunca se saltaría, por lo que la lógica equivalente tendría que proporcionarse poniéndola en un if. Esto se ha dicho antes, pero no del todo en estas palabras, por lo que puede ayudar a alguien más. Intente ejecutar el siguiente fragmento de código. Estoy totalmente a favor del comentario de "no descanso" para mayor claridad.

for a in range(3):
    print(a)
    if a==4: # change value to force break or not
        break
else: #no break  +10 for whoever thought of this decoration
    print('for completed OK')

print('statement after for loop')

"El salto salta fuera del bloque y, por lo tanto, salta 'sobre' la cláusula else" , aunque esto puede ser útil como una forma de "obtener" for:/ else:, realmente no proporciona una justificación para que la palabra clave sea else. Dado el encuadre dado aquí, then:parece que sería mucho más natural. (No hay razones para elseser elegido, dados en otras respuestas - que simplemente no están siempre aquí.)
Marcos Amery

16

Creo que la documentación tiene una gran explicación de lo contrario , continúe

[...] se ejecuta cuando el ciclo termina por agotamiento de la lista (con for) o cuando la condición se vuelve falsa (con while), pero no cuando el ciclo se termina con una declaración de interrupción ".

Fuente: Documentos de Python 2: Tutorial sobre flujo de control


13

Lo leí algo como:

Si aún está en condiciones de ejecutar el ciclo, haga cosas, de lo contrario , haga otra cosa.


Su estado de ánimo aún es útil (+1) aunque está mal, es humano ;-)
Wolf

-1; esta pronunciación de for:/ else:hace que parezca else:que siempre se ejecutará después del ciclo, que no es el caso.
Mark Amery

11

Dado que la parte técnica ha sido bastante respondida, mi comentario es solo en relación con la confusión que produce esta palabra clave reciclada .

Siendo Python un lenguaje de programación muy elocuente , el mal uso de una palabra clave es más notorio. La elsepalabra clave describe perfectamente parte del flujo de un árbol de decisión, "si no puede hacer esto, (de lo contrario) haga eso". Está implícito en nuestro propio idioma.

En cambio, usar esta palabra clave con whiley fordeclaraciones crea confusión. La razón, nuestra carrera como programadores nos ha enseñado que la elsedeclaración reside dentro de un árbol de decisión; su alcance lógico , un contenedor que condicionalmente devuelve un camino a seguir. Mientras tanto, las declaraciones de bucle tienen un objetivo figurativo explícito para alcanzar algo. El objetivo se cumple después de iteraciones continuas de un proceso.

if / else indicar un camino a seguir . Los bucles siguen un camino hasta que se completa el "objetivo" .

El problema es que elsees una palabra que define claramente la última opción en una condición. Los semántica de la palabra son ambos compartían por Python y el lenguaje humano. Pero la palabra else en Lenguaje Humano nunca se usa para indicar las acciones que alguien o algo tomarán después de que algo se complete. Se usará si, en el proceso de completarlo, surge un problema (más como una declaración de interrupción ).

Al final, la palabra clave permanecerá en Python. Está claro que fue un error, más claro cuando cada programador trata de inventar una historia para comprender su uso como un dispositivo mnemotécnico. Me hubiera encantado si hubieran elegido la palabra clave then. Creo que esta palabra clave encaja perfectamente en ese flujo iterativo, la recompensa después del ciclo.

Se parece a esa situación que tiene un niño después de seguir cada paso en el montaje de un juguete: ¿Y ENTONCES qué papá?


Creo que esta respuesta aborda el tema de la confusión de la que estaba hablando el OP. La palabra clave else hace exactamente lo contrario de lo que cabría esperar del significado en inglés de else cuando se adjunta a la acción de for. En teoría, el for ... else podría haber funcionado de manera diferente, ya que terminas en la parte else cuando se rompe el bucle, pero el problema es que usarlo para encontrar el elemento x y manejar el caso donde x es no encontrado, es posible que tenga que usar una bandera u otra prueba después de todo para .. otra construcción
Spacen Jasset

7

Lo leí como "Cuando iterablese agota por completo, y la ejecución está a punto de pasar a la siguiente declaración después de terminar for, se ejecutará la cláusula else". Por lo tanto, cuando la iteración se rompe break, esto no se ejecutará.


6

Estoy de acuerdo, es más como un 'elif no [condición (s) elevando el descanso]'.

Sé que este es un hilo viejo, pero estoy investigando la misma pregunta en este momento, y no estoy seguro de que alguien haya captado la respuesta a esta pregunta en la forma en que la entiendo.

Para mí, hay tres formas de "leer" las declaraciones o elsein , todas las cuales son equivalentes, son:For... elseWhile... else

  1. else == if the loop completes normally (without a break or error)
  2. else == if the loop does not encounter a break
  3. else == else not (condition raising break) (presumiblemente existe tal condición, o no tendría un bucle)

Entonces, esencialmente, el "else" en un bucle es realmente un "elif ..." donde '...' es (1) sin pausa, que es equivalente a (2) NOT [condición (s) que levanta la pausa].

Creo que la clave es que no elsetiene sentido sin el 'descanso', por lo que for...elseincluye:

for:
    do stuff
    conditional break # implied by else
else not break:
    do more stuff

Entonces, los elementos esenciales de un for...elsebucle son los siguientes, y los leería en inglés simple como:

for:
    do stuff
    condition:
        break
else: # read as "else not break" or "else not condition"
    do more stuff

Como han dicho los otros carteles, generalmente se genera un descanso cuando puede localizar lo que está buscando su ciclo, por lo que el else: convierte en "qué hacer si el elemento objetivo no se encuentra".

Ejemplo

También puede usar manejo de excepciones, pausas y bucles todos juntos.

for x in range(0,3):
    print("x: {}".format(x))
    if x == 2:
        try:
            raise AssertionError("ASSERTION ERROR: x is {}".format(x))
        except:
            print(AssertionError("ASSERTION ERROR: x is {}".format(x)))
            break
else:
    print("X loop complete without error")

Resultado

x: 0
x: 1
x: 2
ASSERTION ERROR: x is 2
----------
# loop not completed (hit break), so else didn't run

Ejemplo

Ejemplo simple con un descanso siendo golpeado.

for y in range(0,3):
    print("y: {}".format(y))
    if y == 2: # will be executed
        print("BREAK: y is {}\n----------".format(y))
        break
else: # not executed because break is hit
    print("y_loop completed without break----------\n")

Resultado

y: 0
y: 1
y: 2
BREAK: y is 2
----------
# loop not completed (hit break), so else didn't run

Ejemplo

Ejemplo simple donde no hay interrupción, no hay condición que provoque una interrupción y no se encuentre ningún error.

for z in range(0,3):
     print("z: {}".format(z))
     if z == 4: # will not be executed
         print("BREAK: z is {}\n".format(y))
         break
     if z == 4: # will not be executed
         raise AssertionError("ASSERTION ERROR: x is {}".format(x))
else:
     print("z_loop complete without break or error\n----------\n")

Resultado

z: 0
z: 1
z: 2
z_loop complete without break or error
----------

6

La elsepalabra clave puede ser confuso aquí, y como muchas personas tienen puntas hacia fuera, algo así nobreak, notbreakes más apropiado.

Para entenderlo for ... else ...lógicamente, compárelo con try...except...else, no if...else..., la mayoría de los programadores de Python están familiarizados con el siguiente código:

try:
    do_something()
except:
    print("Error happened.") # The try block threw an exception
else:
    print("Everything is find.") # The try block does things just find.

Del mismo modo, piense breaken un tipo especial de Exception:

for x in iterable:
    do_something(x)
except break:
    pass # Implied by Python's loop semantics
else:
    print('no break encountered')  # No break statement was encountered

La diferencia es pythonimplica except breaky no se puede escribir, por lo que se convierte en:

for x in iterable:
    do_something(x)
else:
    print('no break encountered')  # No break statement was encountered

Sí, sé que esta comparación puede ser difícil y agotadora, pero aclara la confusión.


Debe hacer un enlace al recurso cuando copie de él: las notas de Python de Nick Coghlan .
godaygo

@godaygo gracias por el enlace. Leí y acepto el concepto cuando aprendí Python por primera vez, no memoricé la fuente al escribir la respuesta.
cizixs el

@cizixs ¿No "memorizaste la fuente" pero acabas de incluir oraciones completas de comentarios idénticos al original? Ooookaaaay
Mark Amery

5

Los códigos en el elsebloque de instrucciones se ejecutarán cuando el forciclo no se haya roto.

for x in xrange(1,5):
    if x == 5:
        print 'find 5'
        break
else:
    print 'can not find 5!'
#can not find 5!

De los documentos: romper y continuar declaraciones, y otras cláusulas en bucles

Las declaraciones de bucle pueden tener una cláusula else; se ejecuta cuando el ciclo termina por agotamiento de la lista (con for) o cuando la condición se vuelve falsa (con while), pero no cuando el ciclo se termina con una declaración de interrupción. Esto se ejemplifica con el siguiente ciclo, que busca números primos:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print(n, 'equals', x, '*', n//x)
...             break
...     else:
...         # loop fell through without finding a factor
...         print(n, 'is a prime number')
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3

(Sí, este es el código correcto. Observe atentamente: la cláusula else pertenece al ciclo for, no a la instrucción if).

Cuando se usa con un bucle, la cláusula else tiene más en común con la cláusula else de una declaración try que con la de las declaraciones if: la cláusula else de una declaración try se ejecuta cuando no se produce una excepción, y la cláusula else de un ciclo se ejecuta cuando no se produce una interrupción . Para obtener más información sobre la declaración de prueba y las excepciones, consulte Manejo de excepciones.

La instrucción continue, también tomada de C, continúa con la siguiente iteración del ciclo:

>>> for num in range(2, 10):
...     if num % 2 == 0:
...         print("Found an even number", num)
...         continue
...     print("Found a number", num)
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9

1
Esto no agrega nada y no responde la pregunta, que no es cómo sino por qué .
Aire

5

Aquí hay una manera de pensar que no he visto a nadie más mencionar arriba:

Primero, recuerde que los bucles for son básicamente azúcar sintáctica alrededor de los bucles while. Por ejemplo, el bucle

for item in sequence:
    do_something(item)

puede reescribirse (aproximadamente) como

item = None
while sequence.hasnext():
    item = sequence.next()
    do_something(item)

Segundo, recuerde que los bucles while son básicamente bloques if repetidos. Siempre puede leer un ciclo while como "si esta condición es verdadera, ejecute el cuerpo, luego regrese y verifique nuevamente".

Entonces, while / else tiene mucho sentido: es exactamente la misma estructura que if / else, con la funcionalidad adicional de bucle hasta que la condición se vuelve falsa en lugar de simplemente verificar la condición una vez.

Y luego for / else también tiene perfecto sentido: dado que todos los bucles for son solo azúcar sintáctico encima de los bucles while, solo necesita descubrir cuál es el condicional implícito del bucle while subyacente, y luego lo demás corresponde a cuándo la condición se vuelve falsa.


4

Grandes respuestas son:

  • esto que explica la historia, y
  • Esto le da la cita correcta para facilitar su traducción / comprensión.

Mi nota aquí proviene de lo que Donald Knuth dijo una vez (lo siento, no puedo encontrar referencias) de que hay una construcción donde while-else es indistinguible de if-else, es decir (en Python):

x = 2
while x > 3:
    print("foo")
    break
else:
    print("boo")

tiene el mismo flujo (excluyendo diferencias de bajo nivel) como:

x = 2
if x > 3:
    print("foo")
else:
    print("boo")

El punto es que if-else puede considerarse como azúcar sintáctico para while-else que está implícito breakal final de su ifbloque. La implicación opuesta, ese whileciclo es una extensión if, es más común (es solo una verificación condicional repetida / en bucle), porque a ifmenudo se enseña antes while. Sin embargo, eso no es cierto porque eso significaría elsebloquear en while-else se ejecutaría cada vez que la condición sea falsa.

Para facilitar su comprensión, piense de esa manera:

Sin break, returnetc., el ciclo finaliza solo cuando la condición ya no es verdadera y en ese caso el elsebloque también se ejecutará una vez. En el caso de Python for, debe considerar los forbucles de estilo C (con condiciones) o traducirlos a while.

Otra nota:

El ciclo interno prematuro break, returnetc. hace imposible que la condición se vuelva falsa porque la ejecución saltó fuera del ciclo mientras la condición era verdadera y nunca volvería a verificarla nuevamente.


3

Se podría pensar como, elsecomo en el resto de las cosas, o las otras cosas, que no se hicieron en el bucle.


3
for i in range(3):
    print(i)

    if i == 2:
        print("Too big - I'm giving up!")
        break;
else:
    print("Completed successfully")

"más" aquí es locamente simple, solo significa

1, "si for clausese completa"

for i in range(3):
    print(i)

    if i == 2:
        print("Too big - I'm giving up!")
        break;
if "for clause is completed":
    print("Completed successfully")

Está manejando escribir declaraciones tan largas como "se completa la cláusula for", por lo que presentan "else"

else Aquí hay un si en su naturaleza.

2, sin embargo, ¿qué tal for clause is not run at all

In [331]: for i in range(0):
     ...:     print(i)
     ...: 
     ...:     if i == 9:
     ...:         print("Too big - I'm giving up!")
     ...:         break
     ...: else:
     ...:     print("Completed successfully")
     ...:     
Completed successfully

Entonces, su declaración es una combinación lógica:

if "for clause is completed" or "not run at all":
     do else stuff

o ponerlo de esta manera:

if "for clause is not partially run":
    do else stuff

o de esta manera:

if "for clause not encounter a break":
    do else stuff

else actúa como "transacción" en SQL.
Cálculo el

2

Aquí hay otro caso de uso idiomático además de la búsqueda. Supongamos que desea esperar a que se cumpla una condición, por ejemplo, que se abra un puerto en un servidor remoto, junto con un tiempo de espera. Entonces podría utilizar una while...elseconstrucción como esta:

import socket
import time

sock = socket.socket()
timeout = time.time() + 15
while time.time() < timeout:
    if sock.connect_ex(('127.0.0.1', 80)) is 0:
        print('Port is open now!')
        break
    print('Still waiting...')
else:
    raise TimeoutError()

1

Solo estaba tratando de darle sentido nuevamente. ¡Encontré que lo siguiente ayuda!

• Piense en elseque está emparejado con el ifinterior del bucle (en lugar de con el for): si se cumple la condición, rompa el bucle, de lo contrario, haga esto, ¡excepto que es uno elseemparejado con múltiples ifs!
• Si no hay ningún ifs satisfecho en absoluto, entonces haga lo else.
• ¡Los múltiples ifs también pueden ser considerados como if- elifs!


-2

Considero que la estructura que para (si) A bien B, y para (si) -else es un especial if-else , más o menos . Puede ser útil entender más .

A y B se ejecutan como máximo una vez, que es lo mismo que la estructura if-else.

for (if) puede considerarse como un if especial, que realiza un bucle para tratar de cumplir con la condición if. Una vez que se cumple la condición if , A y break ; De lo contrario , B.


-2

Python usa un bucle else after for y while para que si nada se aplica al loop, algo más suceda. Por ejemplo:

test = 3
while test == 4:
     print("Hello")
else:
     print("Hi")

La salida sería 'Hola' una y otra vez (si estoy en lo correcto).

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.