What does the percentage sign mean?
It's an operator in Python that can mean several things depending on the context. A lot of what follows was already mentioned [or hinted at] in the other answers but I thought it could be helpful to provide a more extensive summary.
%
for Numbers: Modulo operation / Remainder / Rest
The percentage sign is an operator in Python. It's described as:
x % y remainder of x / y
So it gives you the remainder/rest that remains if you "floor divide" x by y. Generally [at least in Python] given a number x
and a divisor y
:
x == y * [x // y] + [x % y]
For example if you divide 5 by 2:
>>> 5 // 2
2
>>> 5 % 2
1
>>> 2 * [5 // 2] + [5 % 2]
5
In general you use the modulo operation to test if a number divides evenly by another number, that's because multiples of a number modulo that number returns 0:
>>> 15 % 5 # 15 is 3 * 5
0
>>> 81 % 9 # 81 is 9 * 9
0
That's how it's used in your example, it cannot be a prime if it's a multiple of another number [except for itself and one], that's what this does:
if n % x == 0:
break
If you feel that n % x == 0
isn't very descriptive you could put it in another function with a more descriptive name:
def is_multiple[number, divisor]:
return number % divisor == 0
...
if is_multiple[n, x]:
break
Instead of
is_multiple
it could also be named evenly_divides
or something similar. That's what is tested here.
Similar to that it's often used to determine if a number is "odd" or "even":
def is_odd[number]:
return number % 2 == 1
def is_even[number]:
return number % 2 == 0
And in some cases it's also used for array/list indexing when wrap-around [cycling] behavior is wanted, then you just modulo the "index" by the "length of the array" to achieve that:
>>> 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
Note that there is also a function for this operator in the standard library
operator.mod
[and the alias operator.__mod__
]:
>>> import operator
>>> operator.mod[5, 2] # equivalent to 5 % 2
1
But there is also the augmented assignment %=
which assigns the result back to the variable:
>>> a = 5
>>> a %= 2 # identical to: a = a % 2
>>> a
1
%
for strings:
printf
-style String Formatting
For strings the meaning is completely different, there it's one way [in my opinion the most limited and ugly] for doing string formatting:
>>> "%s is %s." % ["this", "good"]
'this is good'
Here the %
in the string represents a placeholder followed by a formatting specification. In this case I used %s
which means that it expects a string. Then the
string is followed by a %
which indicates that the string on the left hand side will be formatted by the right hand side. In this case the first %s
is replaced by the first argument this
and the second %s
is replaced by the second argument [good
].
Note that there are much better [probably opinion-based] ways to format strings:
>>> "{} is {}.".format["this", "good"]
'this is good.'
%
in Jupyter/IPython: magic commands
To quote the docs:
To Jupyter users: Magics are specific to and provided by the IPython kernel. Whether magics are available on a kernel is a decision that is made by the kernel developer on a per-kernel basis. To work properly, Magics must use a syntax element which is not valid in the underlying language. For example, the IPython kernel uses the
%
syntax element for magics as%
is not a valid unary operator in Python. While, the syntax element has meaning in other languages.
This is regularly used in Jupyter notebooks and similar:
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]
The %
operator on arrays [in the NumPy / Pandas ecosystem]
The %
operator is still the modulo operator when applied to these arrays, but it returns an array containing the remainder of each element in the 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]]
Customizing the %
operator for your own classes
Of course you can customize how your own classes work when the %
operator is applied to them. Generally you should only use it to implement modulo operations! But that's a guideline, not a hard rule.
Just to provide a simple example that shows how it works:
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]
This example isn't really useful, it just prints and then delegates the operator to the stored value, but it shows that
__mod__
is called when %
is applied to an instance:
>>> a = MyNumber[10]
>>> a % 2
__mod__ called on 'MyNumber[10]'
0
Note that it also works for %=
without explicitly needing to implement __imod__
:
>>> a = MyNumber[10]
>>> a %= 2
__mod__ called on 'MyNumber[10]'
>>> a
0
However you could also implement __imod__
explicitly to overwrite the augmented assignment:
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]
Now %=
is explicitly overwritten to work in-place:
>>> a = MyNumber[10]
>>> a %= 2
__imod__ called on 'MyNumber[10]'
>>> a
MyNumber[0]