¿Cómo se obtiene el xor lógico de dos variables en Python?


648

¿Cómo se obtiene el xor lógico? de dos variables en Python?

Por ejemplo, tengo dos variables que espero sean cadenas. Quiero probar que solo uno de ellos contiene un valor Verdadero (no es Ninguno o la cadena vacía):

str1 = raw_input("Enter string one:")
str2 = raw_input("Enter string two:")
if logical_xor(str1, str2):
    print "ok"
else:
    print "bad"

El ^operador parece ser bit a bit y no está definido en todos los objetos:

>>> 1 ^ 1
0
>>> 2 ^ 1
3
>>> "abc" ^ ""
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for ^: 'str' and 'str'

3
¿Cómo define "xor" para un par de cadenas? ¿Qué sientes que "abc" ^ "" debería devolver que no lo hace?
Mehrdad Afshari

18
Debería devolver True, en lugar de generar una excepción, ya que solo una de las cadenas es True según lo define el tipo bool normal de Python.
Zach Hirsch

38
Me sorprende que Python no tenga un operador infijo llamado "xor", que sería la implementación Pythonic más intuitiva. El uso de "^" es coherente con otros lenguajes, pero no tan legible como la mayoría de Python.
Mark E. Haase

13
@MehrdadAfshari La respuesta obvia a su pregunta es que a xor ase define como (a and not b) or (not a and b)y, por lo tanto a xor b, cuándo ay bson cadenas de caracteres, o cualquier otro tipo, deberían producir cualquier (a and not b) or (not a and b)rendimiento.
Kaz

1
El problema es que la documentación es pobre. ^ es "bitwise exclusivo o", que literalmente interpretado significa bit por bit, no bool por bool. entonces x'FFFF00 '^ x'FFFF00' debería ser x'000000 '. ¿O solo se supone que esto ocurra char por char? emitidos como números? Necesitamos iterar los caracteres de cadena más cortos para que coincidan con la longitud de la cadena más larga. Todo esto debe ser construido en.
mckenzm

Respuestas:


1188

Si ya está normalizando las entradas a booleanos, entonces! = Es xor.

bool(a) != bool(b)

148
Aunque esto es inteligente y breve, no estoy convencido de que esté limpio. Cuando alguien lee esta construcción en el código, ¿es inmediatamente obvio para ellos que se trata de una operación xor? Me sentí obligado a agregar un comentario, una señal para mí de que estoy escribiendo un código poco claro y trato de disculparme con un comentario.

47
Quizás "¿está claro que es un XOR?" Es la pregunta equivocada. Solo intentamos ver si la respuesta a dos preguntas es la misma, y ​​pensamos que usaríamos XOR para implementar eso. Por ejemplo, si queremos asegurarnos de que no estamos comparando manzanas con naranjas, ¿es "if xor (isApple (x), isApple (y))" más claro que "if isApple (x)! = IsApple (y)"? ¡No para mí!
AmigoNico

106
Hay un problema con el uso de "! =" Como xor. Probablemente espere que bool (a)! = Bool (b)! = Bool (c) sea lo mismo que bool (a) ^ bool (b) ^ bool (c). También los lanzamientos a bool, pero recomendaría ^. Para saber qué sucede en el primer ejemplo, busque "encadenamiento de operadores".
elmo

19
@elmo: +1 para señalar la diferencia y +1 para enseñarme qué es el encadenamiento de operadores. Estoy en el campamento que dice que! = No es tan legible como ^.
Mark E. Haase

13
debería ser bool(a) is not bool(b)en su lugar?
ARN

485

Siempre puede usar la definición de xor para calcularla a partir de otras operaciones lógicas:

(a and not b) or (not a and b)

Pero esto es demasiado detallado para mí, y no es particularmente claro a primera vista. Otra forma de hacerlo es:

bool(a) ^ bool(b)

El operador xor en dos booleanos es xor lógico (a diferencia de los ints, donde es bit a bit). Lo cual tiene sentido, ya que booles solo una subclase deint , pero se implementa para tener solo los valores 0y 1. Y xor lógico es equivalente a xor bit a bit cuando el dominio está restringido a 0y1 .

Entonces la logical_xorfunción se implementaría como:

def logical_xor(str1, str2):
    return bool(str1) ^ bool(str2)

Crédito a Nick Coghlan en la lista de correo Python-3000 .


77
gran publicación, pero de todas las formas de nombrar sus parámetros, ¿por qué 'str1' y 'str2'?
SingleNegationElimination

1
@ Tomó por qué no. ¿Quieres decir porque no son muy pitónicos?
orokusaki el

1
@Zach Hirsch ¿Podría usar (no a y b) en lugar de (b y no a) para facilitar la lectura o la definición sería incompatible con xor.
orokusaki 01 de

10
Debería poner las notas primero de esta (not b and a) or (not a and b)manera para que devuelva la cadena si hubiera una, que parece ser la forma pitónica para que funcione la función.
rjmunro

2
@TokenMacGuy: ¿Qué estabas sugiriendo que debería nombrarlos en su lugar?
user541686

180

Bitwise exclusive-or ya está integrado en Python, en el operatormódulo (que es idéntico al ^operador):

from operator import xor
xor(bool(a), bool(b))  # Note: converting to bools is essential

3
Esto es lo que necesitaba. Cuando el malware de ingeniería inversa muchas veces las cadenas se destrozan hasta una operación XOR. Usando este chr (xor (ord ("n"), 0x1A)) = 't'
ril3y

75
Tenga cuidado, esto también es bit a bit: xor(1, 2)retornos 3. Desde la cadena de documentación: xor(a, b) -- Same as a ^ b. recuerde que cualquier cosa importada operatores solo una forma funcional de un operador infijo incorporado existente.
askewchan

55
@askewchan: el booltipo se sobrecarga __xor__para devolver booleanos. Funcionará bien, pero es excesivo cuando bool(a) ^ bool(b)hace exactamente lo mismo.
Martijn Pieters

@MartijnPieters El ^operador llama __xor__internamente.
Quantum7

55
@ Quantum7: sí, no estoy seguro de por qué me estás diciendo esto. Acabo de decir que el booltipo implementa el __xor__método específicamente porque lo ^llama . El punto es que bool(a) ^ bool(b)funciona bien, no hay necesidad de usar la operator.xor()función aquí.
Martijn Pieters

43

Como explicó Zach , puedes usar:

xor = bool(a) ^ bool(b)

Personalmente, estoy a favor de un dialecto ligeramente diferente:

xor = bool(a) + bool(b) == 1

Este dialecto está inspirado en un lenguaje de diagramación lógico que aprendí en la escuela, donde "OR" se denota con un cuadro que contiene ≥1(mayor o igual que 1) y "XOR" se denota con un cuadro que contiene=1 .

Esto tiene la ventaja de implementar correctamente operandos exclusivos o múltiples.

  • "1 = a ^ b ^ c ..." significa que el número de operandos verdaderos es impar. Este operador es "paridad".
  • "1 = a + b + c ..." significa que exactamente un operando es verdadero. Esto es "exclusivo o", que significa "uno para la exclusión de los demás".

12
Entonces, Verdadero + Verdadero + Falso + Verdadero == 3, y 3! = 1, pero Verdadero XOR Verdadero XOR Falso XOR Verdadero == Verdadero. ¿Puedes dar más detalles sobre "implementar correctamente XOR en múltiples operandos"?
tzot

3
@tzot Su ejemplo falla, porque de acuerdo con la solución de ddaa, aplica la adición en solo dos variables a la vez. Entonces, la forma correcta de escribirlo todo tendría que ser (((((True + True)==1)+False)==1)+True)==1. La respuesta dada aquí se generaliza totalmente a múltiples operandos.
ely

66
Además, hay una diferencia entre un XOR de tres vías frente a un conjunto de dos XOR agrupados por orden de operaciones. Entonces 3-WAY-XOR (A, B, C) no es lo mismo que XOR (XOR (A, B), C). Y el ejemplo de ddaa es el primero, mientras que el tuyo supone el segundo.
ely

3
@ Mr.F Su explicación realmente no excusa esta respuesta. En Python, si usted acaba de hacer True + True + False + True, que no consigue 3, y True + True + False + True == 3le da la espalda True, mientras que True + True + False + True == 1da vuelta False. En otras palabras, la respuesta aquí no se generaliza correctamente; para que lo haga, debe hacer un trabajo adicional. Mientras tanto, un simple True ^ True ^ False ^ Truefunciona como se esperaba.
jpmc26

3
@ jpmc26 No entiendo tu comentario. El enfoque de adición está destinado a generalizar la operación en la que desea verificar que exactamente un operando es True, un XOR de aria múltiple. Esta es una operación diferente a, por ejemplo A XOR B XOR ... XOR Z,. En otras palabras, si planea usar la versión basada en la adición, luego de enviar los operandos True + True + False + True, debe esperar que el resultado sea Falsemás de uno True, lo que funciona si la condición lo comprueba == 1.
ely

26
  • Python lógico or:: A or Bdevuelve Asi bool(A)esTrue , de lo contrario devuelveB
  • Python lógico and:: A and Bdevuelve Asi bool(A)esFalse , de lo contrario devuelveB

Para mantener la mayor parte de esa forma de pensar, mi definición lógica o xor sería:

def logical_xor(a, b):
    if bool(a) == bool(b):
        return False
    else:
        return a or b

De esa manera se puede devolver a, bo False:

>>> logical_xor('this', 'that')
False
>>> logical_xor('', '')
False
>>> logical_xor('this', '')
'this'
>>> logical_xor('', 'that')
'that'

55
Esto me parece malo, o al menos extraño. Ninguno de los otros operadores lógicos integrados devuelve uno de los tres valores posibles.
Zach Hirsch

2
@Zach Hirsch: Por eso dije "mantener la mayor parte de esa forma de pensar", ya que no hay buenos resultados cuando ambos son verdaderos o falsos
nosklo el

La operación lógica debe devolver un valor lógico, por lo que el segundo "retorno aob" parece extraño, por lo que el segundo retorno debe devolver True.
Denis Barmenkov

99
@Denis Barmenkov: Bueno, tenga en cuenta que los operadores lógicos de Python andy orno devolverán el valor lógico. 'foo' and 'bar'vuelve 'bar'...
nosklo

66
A primera vista, las 2 respuestas anteriores parecen ser las mejores, pero pensándolo bien, esta es en realidad la única verdaderamente correcta, es decir, es la única que proporciona un ejemplo de una xorimplementación que es coherente con el incorporado andy or. Sin embargo, por supuesto, en situaciones prácticas, bool(a) ^ bool(b)o incluso a ^ b(si ay bson conocidos por ser bool) son más concisa, por supuesto.
Erik Kaplun

23

He probado varios enfoques y not a != (not b)parecía ser el más rápido.

Aquí hay algunas pruebas

%timeit not a != (not b)
10000000 loops, best of 3: 78.5 ns per loop

%timeit bool(a) != bool(b)
1000000 loops, best of 3: 343 ns per loop

%timeit not a ^ (not b)
10000000 loops, best of 3: 131 ns per loop

Editar: los ejemplos 1 y 3 anteriores faltan paréntesis, por lo que el resultado es incorrecto. Nuevos resultados + truth()función como sugirió ShadowRanger.

%timeit  (not a) ^  (not b)   # 47 ns
%timeit  (not a) != (not b)   # 44.7 ns
%timeit truth(a) != truth(b)  # 116 ns
%timeit  bool(a) != bool(b)   # 190 ns

66
Son 100 ns de mi vida que no volveré ;-)
Arel

44
Para un tiempo intermedio, puede hacerlo from operator import truthen la parte superior del módulo y probar truth(a) != truth(b). boolser un constructor tiene una sobrecarga inevitable en el nivel C (debe aceptar argumentos como el equivalente *args, **kwargsy analizar tupley dictextraerlos), donde truth(siendo una función) puede usar una ruta optimizada que no requiere un tupleo a dict, y se ejecuta en aproximadamente la mitad del tiempo de las boolsoluciones basadas (pero aún más tiempo que las notsoluciones basadas).
ShadowRanger

9

Hilo gratificante:

Una idea más antigua ... Solo intente la (puede ser) expresión pitónica «no es» para obtener un comportamiento de «xor» lógico

La tabla de verdad sería:

>>> True is not True
False
>>> True is not False
True
>>> False is not True
True
>>> False is not False
False
>>>

Y para su cadena de ejemplo:

>>> "abc" is not  ""
True
>>> 'abc' is not 'abc' 
False
>>> 'abc' is not '' 
True
>>> '' is not 'abc' 
True
>>> '' is not '' 
False
>>> 

Sin embargo; como indicaron anteriormente, depende del comportamiento real que desee extraer de cualquier par de cuerdas, porque las cuerdas no son boletines ... y aún más: si se «sumerge en Python» encontrará «La naturaleza peculiar de" y "y" o "» http://www.diveintopython.net/power_of_introspection/and_or.html

Lo siento, mi inglés escrito, no es mi lengua materna.

Saludos.


También suelo leerlo como "estrictamente diferente". Esto se debe a que algunos lenguajes utilizan para implementar la operación bit a bit de la representación binaria y toman el papel de la operación bit a bit resultante. Supongo que su respuesta es más "tipo-a prueba de balas" porque se extiende más allá del espacio booleano.
platillo

Me refiero al hecho de que su respuesta cubre el caso de comparar None, False, '' como diferente es lo distintivo. Por ejemplo: bool (False)! = Bool ('') sin embargo False is '' "está más de acuerdo con esta semántica de" estrictamente diferente "
platillo

8

Python tiene un operador OR exclusivo bit a bit, es ^:

>>> True ^ False
True
>>> True ^ True
False
>>> False ^ True
True
>>> False ^ False
False

Puede usarlo convirtiendo las entradas a booleanos antes de aplicar xor ( ^):

bool(a) ^ bool(b)

(Editado - gracias Arel)


Su respuesta debe aclarar que ^es un xor bit a bit (no xor lógico como la pregunta formulada). bool(2) ^ bool(3)da una respuesta diferente que bool(2 ^ 3).
Arel

1
@Arel Pero ese no es el caso. a ^ bes polimorfo Si ay bson boolinstancias, el resultado también lo será bool. Este comportamiento difícilmente se puede llamar un xor "bit a bit".
Alfe

@Alfe, el punto importante es que los valores se deben convertir a booleanos primero. La documentación de Python se define ^como bit a bit, aunque es un punto interesante para el que se conservan los tipos booly los inttipos. Nota: True ^ 2es 3, demostrando cómo es de hecho bit a bit.
Arel

@Arel Sí, el bool ^ intcaso es poner todo en intprimer lugar. Aún así, Python ha incorporado el ^operador para muchos bits inty para el bit representado en a bool, por lo que ambos son bit a bit , pero el bit xor para un solo bit es el xor lógico para booleanos.
Alfe

Siempre odio el uso de este operador, aunque entiendo que xor, desde una formación en ingeniería, para mí esto instintivamente se siente como un poder matemático, es 2^3 = pow(2,3)decir, que siempre comento explícitamente para evitar confusiones.
Nicholas Hamilton

8

Como no veo la variante simple de xor usando argumentos variables y solo operando con valores de Verdad Verdadero o Falso, lo lanzaré aquí para que cualquiera lo use. Es como lo han señalado otros, bastante (por no decir muy) directo.

def xor(*vars):
    sum = False
    for v in vars:
        sum = sum ^ bool(v)
    return sum

Y el uso también es sencillo:

if xor(False, False, True, False):
    print "Hello World!"

Como este es el XOR lógico n-ario generalizado, su valor de verdad será verdadero siempre que el número de operandos verdaderos sea impar (y no solo cuando exactamente uno es verdadero, este es solo un caso en el que XOR n-ario es verdadero).

Por lo tanto, si está buscando un predicado n-ario que solo sea verdadero cuando exactamente uno de sus operandos lo es, es posible que desee usar:

def isOne(*vars):
    sum = False
    for v in vars:
        if sum and v:
            return False
        else:
            sum = sum or v
    return sum

Para mejorar esta respuesta: (bool(False) is False) == True. Simplemente puede usar Falseen esas líneas.
pathunstrom

7

Exclusivo O se define de la siguiente manera

def xor( a, b ):
    return (a or b) and not (a and b)

2
eso devolvería True para xor ('this', '') y para seguir el camino de Python, debería devolver 'this'.
nosklo

@nosklo: tómalo con el BDFL, por favor, no yo. Dado que Python devuelve True, entonces ese debe ser el camino de Python.
S.Lott

2
Me refiero a la coherencia con los otros operadores lógicos de Python: Python no devuelve True cuando lo hago ('this' o ''), devuelve 'this'. Pero en su función xor ('this', '') devuelve True. Debería devolver 'this' como lo hace el "o" python incorporado.
nosklo

10
Python andy orhacer cortocircuito. Cualquier xorimplementación no puede provocar un cortocircuito, por lo que ya existe una discrepancia; por lo tanto, no hay razón para que xorfuncione como and+ ordo.
tzot

7

A veces me encuentro trabajando con 1 y 0 en lugar de valores booleanos True y False. En este caso, xor se puede definir como

z = (x + y) % 2

que tiene la siguiente tabla de verdad:

     x
   |0|1|
  -+-+-+
  0|0|1|
y -+-+-+
  1|1|0|
  -+-+-+

7

Sé que es tarde, pero pensé y podría valer la pena, solo por documentación. Quizás esto funcionaría: np.abs(x-y)la idea es que

  1. si x = Verdadero = 1 e y = Falso = 0, entonces el resultado sería | 1-0 | = 1 = Verdadero
  2. si x = False = 0 ey = False = 0, entonces el resultado sería | 0-0 | = 0 = False
  3. si x = Verdadero = 1 ey = Verdadero = 1, entonces el resultado sería | 1-1 | = 0 = Falso
  4. si x = False = 0 e y = True = 1, entonces el resultado sería | 0-1 | = 1 = True

7

Simple, fácil de entender:

sum( (bool(a), bool(b) ) == 1

Si lo que busca es una opción exclusiva, puede expandirse a múltiples argumentos:

sum( bool(x) for x in y ) % 2 == 1

1
sum(map(bool, y)) % 2 == 1
warvariuc

6

¿Qué tal esto?

(not b and a) or (not a and b)

dará asi bes falso
dará bsi aes falso
daráFalse contrario

O con la expresión ternaria Python 2.5+:

(False if a else b) if b else a

6

Algunas de las implementaciones sugeridas aquí causarán una evaluación repetida de los operandos en algunos casos, lo que puede provocar efectos secundarios no deseados y, por lo tanto, debe evitarse.

Dicho esto, una xorimplementación que devuelve Trueo Falsees bastante simple; uno que devuelve uno de los operandos, si es posible, es mucho más complicado, porque no existe consenso sobre qué operando debe ser el elegido, especialmente cuando hay más de dos operandos. Por ejemplo, debería xor(None, -1, [], True)regresar None, []oFalse ? Apuesto a que cada respuesta parece ser la más intuitiva para algunas personas.

Para el resultado Verdadero o Falso, hay hasta cinco opciones posibles: devolver el primer operando (si coincide con el resultado final en valor, de lo contrario booleano), devolver la primera coincidencia (si existe al menos uno, de lo contrario booleano), devolver el último operando (si ... más ...), devolver la última coincidencia (si ... más ...), o siempre devolver boolean. En total, eso es 5 ** 2 = 25 sabores de xor.

def xor(*operands, falsechoice = -2, truechoice = -2):
  """A single-evaluation, multi-operand, full-choice xor implementation
  falsechoice, truechoice: 0 = always bool, +/-1 = first/last operand, +/-2 = first/last match"""
  if not operands:
    raise TypeError('at least one operand expected')
  choices = [falsechoice, truechoice]
  matches = {}
  result = False
  first = True
  value = choice = None
  # avoid using index or slice since operands may be an infinite iterator
  for operand in operands:
    # evaluate each operand once only so as to avoid unintended side effects
    value = bool(operand)
    # the actual xor operation
    result ^= value
    # choice for the current operand, which may or may not match end result
    choice = choices[value]
    # if choice is last match;
    # or last operand and the current operand, in case it is last, matches result;
    # or first operand and the current operand is indeed first;
    # or first match and there hasn't been a match so far
    if choice < -1 or (choice == -1 and value == result) or (choice == 1 and first) or (choice > 1 and value not in matches):
      # store the current operand
      matches[value] = operand
    # next operand will no longer be first
    first = False
  # if choice for result is last operand, but they mismatch
  if (choices[result] == -1) and (result != value):
    return result
  else:
    # return the stored matching operand, if existing, else result as bool
    return matches.get(result, result)

testcases = [
  (-1, None, True, {None: None}, [], 'a'),
  (None, -1, {None: None}, 'a', []),
  (None, -1, True, {None: None}, 'a', []),
  (-1, None, {None: None}, [], 'a')]
choices = {-2: 'last match', -1: 'last operand', 0: 'always bool', 1: 'first operand', 2: 'first match'}
for c in testcases:
  print(c)
  for f in sorted(choices.keys()):
    for t in sorted(choices.keys()):
      x = xor(*c, falsechoice = f, truechoice = t)
      print('f: %d (%s)\tt: %d (%s)\tx: %s' % (f, choices[f], t, choices[t], x))
  print()

5

Mucha gente, incluido yo mismo, necesita una xorfunción que se comporte como un circuito xor de entrada n, donde n es variable. (Ver https://en.wikipedia.org/wiki/XOR_gate ). La siguiente función simple implementa esto.

def xor(*args):
   """
   This function accepts an arbitrary number of input arguments, returning True
   if and only if bool() evaluates to True for an odd number of the input arguments.
   """

   return bool(sum(map(bool,args)) % 2)

Muestra de E / S a continuación:

In [1]: xor(False, True)
Out[1]: True

In [2]: xor(True, True)
Out[2]: False

In [3]: xor(True, True, True)
Out[3]: True

5

Para obtener el xor lógico de dos o más variables en Python:

  1. Convertir entradas a booleanos
  2. Use el operador xor bit a bit ( ^o operator.xor)

Por ejemplo,

bool(a) ^ bool(b)

Cuando convierte las entradas a booleanos, xor bit a bit se convierte en xor lógico .

Tenga en cuenta que la respuesta aceptada es incorrecta: != no es lo mismo que xor en Python debido a la sutileza del encadenamiento del operador .

Por ejemplo, el xor de los tres valores siguientes es incorrecto cuando se usa !=:

True ^  False ^  False  # True, as expected of XOR
True != False != False  # False! Equivalent to `(True != False) and (False != False)`

(PD: intenté editar la respuesta aceptada para incluir esta advertencia, pero mi cambio fue rechazado).


4

Es fácil cuando sabes lo que hace XOR:

def logical_xor(a, b):
    return (a and not b) or (not a and b)

test_data = [
  [False, False],
  [False, True],
  [True, False],
  [True, True],
]

for a, b in test_data:
    print '%r xor %s = %r' % (a, b, logical_xor(a, b))

4

Esto obtiene el XOR lógico exclusivo para dos (o más) variables

str1 = raw_input("Enter string one:")
str2 = raw_input("Enter string two:")

any([str1, str2]) and not all([str1, str2])

El primer problema con esta configuración es que lo más probable es que atraviese la lista completa dos veces y, como mínimo, verifique dos veces al menos uno de los elementos. Por lo tanto, puede aumentar la comprensión del código, pero no se presta a la velocidad (que puede diferir insignificantemente según su caso de uso).

El segundo problema con esta configuración es que verifica la exclusividad independientemente del número de variables. Al principio, esto puede considerarse como una característica, pero el primer problema se vuelve mucho más significativo a medida que aumenta el número de variables (si alguna vez lo hacen).


4

Xor está ^en Python. Vuelve :

  • Un bit xor para ints
  • Xor lógico para bools
  • Una unión exclusiva para sets
  • Resultados definidos por el usuario para las clases que implementa __xor__.
  • TypeError para tipos indefinidos, como cadenas o diccionarios.

Si tiene la intención de usarlos en cadenas de todos modos, al transmitirlos, boolsu operación no es ambigua (también podría significar set(str1) ^ set(str2)).



3

Así es como codificaría cualquier tabla de verdad. Para xor en particular tenemos:

| a | b  | xor   |             |
|---|----|-------|-------------|
| T | T  | F     |             |
| T | F  | T     | a and not b |
| F | T  | T     | not a and b |
| F | F  | F     |             |

Basta con mirar los valores T en la columna de respuesta y unir todos los casos verdaderos con lógico o. Entonces, esta tabla de verdad se puede producir en el caso 2 o 3. Por lo tanto,

xor = lambda a, b: (a and not b) or (not a and b)

-6

Podemos encontrar fácilmente xor de dos variables usando:

def xor(a,b):
    return a !=b

Ejemplo:

xor (verdadero, falso) >>> verdadero


1
o xor("hey", "there")>>> Es cierto, pero eso no es lo que queremos
Mayou36
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.