为什么需要三种操作方式?
(我用乘法举例)
第一种方式:
df['a'] * 5
第二种方式:
df['a'].mul(5)
第三种方式:
df['a'].__mul__(5)
两个还不够吗,不需要mul,我想知道它能不能像正常方式一样,像一个整数
第一种方式:
3 * 5
第二种方式:
(3).__mul__(5)
但是在 inetger 的常规基础上:
(3).mul(5)
会坏掉。
我很好奇,为什么我们在 Pandas 中需要这么多东西,加法、减法和除法也是如此。
请您参考如下方法:
* 和 mul 做同样的事情,但是 __mul__ 是不同的。
* 和 mul 在委托(delegate)给 __mul__ 之前执行一些检查。您应该了解两件事。
未实现
有一个特殊的单例值 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
这就是为什么在一般情况下,您应该避免直接调用 dunder(魔术)方法:您绕过了 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 甚至不先看看;它立即委托(delegate)给 Derived.__rmul__。
为了完整起见,在 pandas 的上下文中,* 和 mul 之间是一个区别:mul 是一个函数,因此可以在变量中传递并独立使用。例如:
import pandas as pd
pandas_mul = pd.DataFrame.mul
pandas_mul(pd.DataFrame([[1]]), pd.DataFrame([[2]]))
另一方面,这会失败:
*(pd.DataFrame([[1]]), pd.DataFrame([[2]]))
