Skip to main content
 首页 » 编程设计

python之为什么我们需要三种不同的方式来操作 pandas

2025年05月04日20grandyang

为什么需要三种操作方式?

(我用乘法举例)

第一种方式:

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__ 之前执行一些检查。您应该了解两件事。

  1. 未实现

有一个特殊的单例值 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 执行的某些检查。

  1. 派生类运算符处理

如果您尝试执行类似 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]]))