Ответ 1
Когда Python пытается умножить два объекта, он сначала пытается вызвать метод __mul__()
левого объекта. Если левый объект не имеет __mul__()
(или метод возвращает NotImplemented
, указывая, что он не работает с соответствующим операндом), то Python хочет знать, может ли правильный объект выполнить умножение. Если правый операнд того же типа, что и левый, Python знает, что это невозможно, потому что, если левый объект не может этого сделать, другой объект того же типа, безусловно, тоже не может.
Если два объекта разных типов, то Python считает, что это стоит попробовать. Однако ему нужен какой-то способ сообщить нужному объекту, что он является правильным объектом в операции, если операция не является коммутативной. (Умножение, конечно, но не все операторы, и в любом случае *
не всегда используется для умножения!) Поэтому он вызывает __rmul__()
вместо __mul__()
.
В качестве примера рассмотрим следующие два утверждения:
print "nom" * 3
print 3 * "nom"
В первом случае Python вызывает метод string __mul__()
. Строка знает, как умножить себя на целое число, так что все хорошо. Во втором случае целое число не знает, как умножить себя на строку, поэтому его __mul()__
возвращает NotImplemented
и __rmul()__
строка __rmul()__
. Он знает, что делать, и вы получите тот же результат, что и в первом случае.
Теперь мы можем видеть, что __rmul()__
позволяет содержать все особенности специального умножения строк в классе str
, так что другим типам (таким как целые числа) не нужно ничего знать о строках, чтобы иметь возможность их умножать на них. Через сто лет (при условии, что Python все еще используется) вы сможете определить новый тип, который можно умножить на целое число в любом порядке, даже если класс int
ничего не знал о нем более столетия.
Кстати, строковый класс __mul__()
имеет ошибку в некоторых версиях Python. Если он не знает, как умножить себя на объект, он вызывает TypeError
вместо возврата NotImplemented
. Это означает, что вы не можете умножить строку на определенный пользователем тип, даже если этот определенный тип имеет __rmul__()
, потому что строка никогда не дает ей шанс. Пользовательский тип должен идти первым (например, Foo() * 'bar'
вместо 'bar' * Foo()
), поэтому __mul__()
его __mul__()
. Кажется, они исправили это в Python 2.7 (я проверял это и в Python 3.2), но в Python 2.6.6 есть ошибка.