Saltar al contenido

operadores – ¿Qué significa el signo de porcentaje en Python?

octubre 18, 2021
apple touch icon@2

¿Qué significa el signo de porcentaje?

Es un operador en Python que puede significar varias cosas según el contexto. Mucho de lo que sigue ya se mencionó (o insinuó) en las otras respuestas, pero pensé que podría ser útil proporcionar un resumen más extenso.

% para números: operación de módulo / resto / reposo

El signo de porcentaje es un operador en Python. Se describe como:

x % y       remainder of x / y

Entonces te da la resto / resto que permanece si «divide el piso» 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 divide 5 entre 2:

>>> 5 // 2
2
>>> 5 % 2
1

>>> 2 * (5 // 2) + (5 % 2)
5

En general, usa la operación módulo para probar si un número se divide uniformemente por otro número, eso es porque los múltiplos de un número módulo ese número devuelve 0:

>>> 15 % 5  # 15 is 3 * 5
0

>>> 81 % 9  # 81 is 9 * 9
0

Así es como se usa en su ejemplo, no puede ser primo si es un múltiplo de otro número (excepto por sí mismo y uno), eso es lo que hace esto:

if n % x == 0:
    break

Si sientes eso n % x == 0 no es muy descriptivo, podría ponerlo en otra función con un nombre más descriptivo:

def is_multiple(number, divisor):
    return number % divisor == 0

...

if is_multiple(n, x):
    break

En lugar de is_multiple también podría ser nombrado evenly_divides o algo similar. Eso es lo que se prueba aquí.

Similar a eso, a menudo se usa para determinar si un número es «par» o «impar»:

def is_odd(number):
    return number % 2 == 1

def is_even(number):
    return number % 2 == 0

Y en algunos casos también se usa para la indexación de matrices / listas cuando se desea un comportamiento envolvente (ciclo), luego simplemente modula el «índice» por la «longitud de la matriz» para lograr eso:

>>> l = [0, 1, 2]
>>> length = len(l)
>>> for index in range(10):
...     print(l[index % length])
0
1
2
0
1
2
0
1
2
0

Tenga en cuenta 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)  # equivalent to 5 % 2
1

Pero también está la asignación aumentada %= que asigna el resultado a la variable:

>>> a = 5
>>> a %= 2  # identical to: a = a % 2
>>> a
1

Para las cadenas, el significado es completamente diferente, ahí está de una sola mano (en mi opinión, el más limitado y feo) para hacer formato de cadena:

>>> "%s is %s." % ("this", "good") 
'this is good'

Aquí el % en la cadena representa un marcador de posición seguido de una especificación de formato. En este caso usé %s lo que significa que espera una cadena. Luego, la cadena es seguida por un % lo que indica que la cadena en el lado izquierdo será formateada por el lado derecho. En este caso el primero %s es reemplazado por el primer argumento this y el segundo %s es reemplazado por el segundo argumento (good).

Tenga en cuenta que hay formas mucho mejores (probablemente basadas en opiniones) de formatear cadenas:

>>> "{} is {}.".format("this", "good")
'this is good.'

% en Jupyter / IPython: comandos mágicos

Para citar el docs:

Para los usuarios de Jupyter: Las magias son específicas y proporcionadas por el kernel de IPython. Si las magias están disponibles en un kernel es una decisión que toma el desarrollador del kernel por kernel. Para que funcione correctamente, Magics debe utilizar un elemento de sintaxis que no sea válido en el idioma subyacente. Por ejemplo, el kernel de IPython usa el % elemento de sintaxis para magias como % no es un operador unario válido en Python. Mientras que, el elemento de sintaxis tiene significado en otros idiomas.

Esto se usa regularmente en cuadernos de Jupyter y similares:

In [1]:  a = 10
         b = 20
         %timeit a + b   # one % -> line-magic

54.6 ns ± 2.7 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [2]:  %%timeit  # two %% -> cell magic 
         a ** b

362 ns ± 8.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

los % operador en matrices (en el ecosistema NumPy / Pandas)

los % El operador sigue siendo el operador de módulo cuando se aplica a estas matrices, pero devuelve una matriz que contiene el resto de cada elemento de la matriz:

>>> 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, puede personalizar cómo funcionan sus propias clases cuando el % se les aplica el operador. Generalmente tu deberían ¡Úselo solo para implementar operaciones de módulo! Pero esa es una guía, no una regla estricta.

Solo para proporcionar un ejemplo simple que muestra cómo funciona:

class MyNumber(object):
    def __init__(self, value):
        self.value = value

    def __mod__(self, other):
        print("__mod__ called on '{!r}'".format(self))
        return self.value % other

    def __repr__(self):
        return "{self.__class__.__name__}({self.value!r})".format(self=self)

Este ejemplo no es realmente útil, solo imprime y luego delega el operador al valor almacenado, pero muestra que __mod__ se llama cuando % se aplica a una instancia:

>>> a = MyNumber(10)
>>> a % 2
__mod__ called on 'MyNumber(10)'
0

Tenga en cuenta que también funciona para %= sin necesidad explícita de implementar __imod__:

>>> a = MyNumber(10)
>>> a %= 2
__mod__ called on 'MyNumber(10)'

>>> a
0

Sin embargo, también podría implementar __imod__ explícitamente para sobrescribir la asignación aumentada:

class MyNumber(object):
    def __init__(self, value):
        self.value = value

    def __mod__(self, other):
        print("__mod__ called on '{!r}'".format(self))
        return self.value % other

    def __imod__(self, other):
        print("__imod__ called on '{!r}'".format(self))
        self.value %= other
        return self

    def __repr__(self):
        return "{self.__class__.__name__}({self.value!r})".format(self=self)

Ahora %= se sobrescribe explícitamente para que funcione en el lugar:

>>> a = MyNumber(10)
>>> a %= 2
__imod__ called on 'MyNumber(10)'

>>> a
MyNumber(0)

close