如果一个python对象的类实现了__call__dunder方法,它也可以被调用。所以“类方法行为”和“类对象行为”并不是相互排斥的:
class TwoNumberOperation:
def __init__(self, x1, x2):
self.x1 = x1
self.x2 = x2
def addition(self):
return self(what="add")
def multiplication(self):
return self(what="mult")
def __call__(self, what="add"):
if what == "add":
return self.x1 + self.x2
elif what == "mult":
return self.x1 * self.x2
else:
raise ValueError(f"{what} is not a valid operation")
two_n = TwoNumberOperation(1, 2)
现在我们可以直接调用这个实例了
two_n(what="mult")
# output: 2
或通过显式调用addition 方法:
two_n.addition()
# output: 3
现在是熊猫:
如果我们查看源代码,绘图属性在最后添加到DataFrame 类(link):
class DataFrame(NDFrame):
...
# ----------------------------------------------------------------------
# Add plotting methods to DataFrame
plot = CachedAccessor("plot", pandas.plotting.PlotAccessor)
...
pandas.plotting.PlotAccessor 定义了 DataFrame 类中可用的大多数绘图方法,包括 .line (link)、.bar (link) 等。
它的 __call__ 方法 (link) 进行一些数据操作并将数据转发到绘图后端。它需要一个kind= 参数(这实际上就是为什么你可以使用df.plot(kind="bar") 而不是df.plot.bar())。
与上面的TwoNumberOperation 示例类似,pd.Dataframe.plot.bar 等方法在底层调用了PlotAccessor.__call__ 方法,所以df.line 实际上只是(link)
def line(self, x=None, y=None, **kwargs):
return self(kind="line", x=x, y=y, **kwargs)
所有的魔法都发生在__call__ 方法中!