¿Qué significa el signo de porcentaje?
Es un operador en Python que puede significar varias cosas dependiendo del contexto. Mucho de lo que sigue ya fue mencionado (o insinuado) en las otras respuestas, pero pensé que podría ser útil proporcionar un resumen más extenso.
%
para números: Operación Módulo / Resto
El signo de porcentaje es un operador en Python. Se describe como:
x % y resto de x / y
Por lo tanto, te da el resto que queda si divides x por y. Generalmente (al menos en Python) dado un número x
y un divisor y
:
x == y * (x // y) + (x % y)
Por ejemplo, si divides 5 por 2:
>>> 5 // 2
2
>>> 5 % 2
1
>>> 2 * (5 // 2) + (5 % 2)
5
En general, se utiliza la operación módulo para probar si un número se divide de manera uniforme por otro número, eso se debe a que los múltiplos de un número módulo ese número devuelven 0:
>>> 15 % 5 # 15 es 3 * 5
0
>>> 81 % 9 # 81 es 9 * 9
0
Así es como se usa en tu ejemplo, no puede ser un número primo si es un múltiplo de otro número (excepto por él mismo y uno), eso es lo que hace esto:
if n % x == 0:
break
Si sientes que n % x == 0
no es muy descriptivo, podrías ponerlo en otra función con un nombre más descriptivo:
def es_multiplo(numero, divisor):
return numero % divisor == 0
...
if es_multiplo(n, x):
break
En lugar de es_multiplo
también podría llamarse divide_uniformemente
o algo similar. Eso es lo que se prueba aquí.
Similar a eso, a menudo se usa para determinar si un número es "impar" o "par":
def es_impar(numero):
return numero % 2 == 1
def es_par(numero):
return numero % 2 == 0
Y en algunos casos también se utiliza para indexar arreglos/listas cuando se quiere un comportamiento de envoltura (ciclismo), entonces simplemente haces módulo del "índice" por la "longitud del arreglo" para lograr eso:
>>> l = [0, 1, 2]
>>> longitud = len(l)
>>> for indice in range(10):
... print(l[indice % longitud])
0
1
2
0
1
2
0
1
2
0
Nota que también hay una función para este operador en la biblioteca estándar operator.mod
(y el alias operator.__mod__
):
>>> import operator
>>> operator.mod(5, 2) # equivalente a 5 % 2
1
Pero también está la asignación aumentada %=
que asigna el resultado de nuevo a la variable:
>>> a = 5
>>> a %= 2 # idéntico a: a = a % 2
>>> a
1
Para strings, el significado es completamente diferente, allí es una forma (en mi opinión la más limitada y fea) de hacer formateo de strings:
>>> "%s es %s." % ("esto", "bueno")
'esto es bueno'
Aquí el %
en el string representa un marcador seguido de una especificación de formato. En este caso utilicé %s
que significa que espera un string. Luego, el string va seguido por un %
que indica que el string del lado izquierdo será formateado por el lado derecho. En este caso, el primer %s
es reemplazado por el primer argumento esto
y el segundo %s
es reemplazado por el segundo argumento (bueno
).
Nota que hay formas mucho mejores (probablemente basadas en opiniones) de formatear strings:
>>> "{} es {}.".format("esto", "bueno")
'esto es bueno.'
%
en Jupyter/IPython: comandos mágicos
Para citar la documentación:
Para usuarios de Jupyter: Los comandos mágicos son específicos y proporcionados por el núcleo de IPython. Si los comandos mágicos están disponibles en un núcleo es una decisión que toma el desarrollador del núcleo caso por caso. Para funcionar correctamente, los comandos mágicos deben usar un elemento de sintaxis que no sea válido en el lenguaje subyacente. Por ejemplo, el núcleo de IPython utiliza el elemento de sintaxis %
para comandos mágicos ya que %
no es un operador unario válido en Python. Mientras que, el elemento de sintaxis tiene un significado en otros lenguajes.
Esto se usa regularmente en cuadernos Jupyter y similares:
In [1]: a = 10
b = 20
%timeit a + b # un % -> magia de línea
54.6 ns ± 2.7 ns por ciclo (media ± desviación estándar de 7 corridas, 10000000 ciclos cada)
In [2]: %%timeit # dos %% -> magia de celda
a ** b
362 ns ± 8.4 ns por ciclo (media ± desviación estándar de 7 corridas, 1000000 ciclos cada)
El operador %
en arrays (en el ecosistema de NumPy / Pandas)
El operador %
sigue siendo el operador módulo cuando se aplica a estos arrays, pero devuelve un array con el resto de cada elemento en el array:
>>> import numpy as np
>>> a = np.arange(10)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> a % 2
array([0, 1, 0, 1, 0, 1, 0, 1, 0, 1])
Personalizando el operador %
para tus propias clases
Por supuesto, puedes personalizar cómo funcionan tus propias clases cuando se aplica el operador %
a ellas. ¡Generalmente deberías usarlo solo para implementar operaciones módulo! Pero eso es una pauta, no una regla estricta.
Solo para proporcionar un ejemplo simple que muestra cómo funciona:
class MiNumero(object):
def __init__(self, valor):
self.valor = valor
def __mod__(self, otro):
print("__mod__ llamado en '{!r}'".format(self))
return self.valor % otro
def __repr__(self):
return "{self.__class__.__name__}({self.valor!r})".format(self=self)
Este ejemplo no es realmente útil, simplemente imprime y luego delega el operador al valor almacenado, pero muestra que se llama a __mod__
cuando se aplica %
a una instancia:
>>> a = MiNumero(10)
>>> a % 2
__mod__ llamado en 'MiNumero(10)'
0
Nota que también funciona para %=
sin necesidad explícita de implementar __imod__
:
>>> a = MiNumero(10)
>>> a %= 2
__mod__ llamado en 'MiNumero(10)'
>>> a
0
Sin embargo, también podrías implementar __imod__
explícitamente para sobrescribir la asignación aumentada:
class MiNumero(object):
def __init__(self, valor):
self.valor = valor
def __mod__(self, otro):
print("__mod__ llamado en '{!r}'".format(self))
return self.valor % otro
def __imod__(self, otro):
print("__imod__ llamado en '{!r}'".format(self))
self.valor %= otro
return self
def __repr__(self):
return "{self.__class__.__name__}({self.valor!r})".format(self=self)
Ahora %=
está sobrescrito explícitamente para funcionar en el lugar:
>>> a = MiNumero(10)
>>> a %= 2
__imod__ llamado en 'MiNumero(10)'
>>> a
MiNumero(0)