*
и mul
делают одно и то же, но __mul__
отличается.
*
и mul
выполняют некоторые проверки перед делегированием __mul__
.Есть две вещи, о которых вы должны знать.
NotImplemented
Существует специальное одноэлементное значение NotImplemented
, которое возвращается классом __mul__
вслучаи, когда он не может обработать другой операнд.Затем он говорит Python попробовать __rmul__
.Если это тоже не помогает, то генерируется общий TypeError
.Если вы используете __mul__
напрямую, вы не получите эту логику.Обратите внимание:
class TestClass:
def __mul__(self, other):
return NotImplemented
TestClass() * 1
Вывод:
TypeError: unsupported operand type(s) for *: 'TestClass' and 'int'
Сравните это с этим:
TestClass().__mul__(1)
Вывод:
NotImplemented
Вот почемуВ общем, вы должны избегать прямого вызова магических (магических) методов: вы пропускаете определенные проверки, которые выполняет Python.
Обработка оператора производного класса
Когда вы пытаетесь выполнить что-то вроде Base() * Derived()
, где Derived
наследует от Base
, вы ожидаете, что Base.__mul__(Derived())
будет вызван первым.Это может создавать проблемы, поскольку Derived.__mul__
с большей вероятностью знает, как обрабатывать такие ситуации.
Поэтому, когда вы используете *
, Python проверяет, является ли тип правого операнда более производным, чем тип левого, иесли это так, то вызывает метод __rmul__
правого операнда напрямую.
Наблюдение:
class Base:
def __mul__(self, other):
print('base mul')
class Derived(Base):
def __rmul__(self, other):
print('derived rmul')
Base() * Derived()
Вывод:
derived rmul
Обратите внимание, что даже если Base.__mul__
невернуть NotImplemented
и может четко обрабатывать объект типа Derived
, Python даже даже не смотрит на него первым;он делегирует Derived.__rmul__
немедленно.
Для полноты, равно одно отличие между *
и mul
, в контексте pandas
: mul
является функциейи, следовательно, может передаваться в переменной и использоваться независимо.Например:
import pandas as pd
pandas_mul = pd.DataFrame.mul
pandas_mul(pd.DataFrame([[1]]), pd.DataFrame([[2]]))
С другой стороны, это не удастся:
*(pd.DataFrame([[1]]), pd.DataFrame([[2]]))