问题描述
假设我们使用的是 python 3.x
或更新版本,而不是 python 2.x
Python 和许多语言一样,有一个点运算符:
# Create a new instance of the Rectangle class
robby = Rectangle(3,10)
# INVOKE THE DOT OPERATOR
x = robby.length
Python 的点运算符有时实现为 __getattribute__
。@H_502_19@
以下等效于 x = robby.length
:
x = Rectangle.__getattribute__(robby,"length")
但是,点运算符不总是实现为 __getattribute__
。
Python 有“魔法方法”@H_502_19@魔术方法是名称以两个下划线字符开头和结尾的任何方法。@H_502_19@__len__()
是魔法方法的一个例子。
你可以通过执行下面的代码得到python的大部分魔术方法的列表:
print("\n".join(filter(lambda s: s.startswith("__"),dir(int))))
输出为:
__abs__
__add__
__and__
__bool__
__ceil__
__class__
__delattr__
__dir__
__divmod__
__doc__
__eq__
__float__
[... truncated / abridged ...]
__rtruediv__
__rxor__
__setattr__
__sizeof__
__str__
__sub__
__subclasshook__
__truediv__
__trunc__
__xor__
假设我们写了一个名为 Rectangle
的类,object
的子类。@H_502_19@
然后我尝试在 object.__getattribute__
类中覆盖 Rectangle
,通常会失败。
以下显示了一个类的示例,其中 python 有时会忽略重写的点运算符:
class Klass:
def __getattribute__(self,attr_name):
return print
obj = Klass()
obj.append() # WORKS FINE. `obj.append == print`
obj.delete() # WORKS FINE. `obj.delete == print`
obj.length # WORKS FINE
obj.x # WORKS FINE
# None of the following work,because they
# invoke magic methods.
# The following line is similar to:
# x = Klass.__len__(obj)
len(obj)
# obj + 5
# is similar to:
# x = Klass.__add__(obj,5)
x = obj + 5
# The following line is similair to:
# x = Klass.__radd__(obj,2)
x = 2 + obj
有不止一种方法可以覆盖python的点运算符。@H_502_19@ 什么是可读、干净和一致的方法的例子?
通过一致,我的意思是每当在源代码中使用 .
时,我们的自定义点运算符都会被调用,无论该方法是否是魔术方法与否。
我不愿意在阳光下手动输入每一个神奇的方法。@H_502_19@ 我不想看到像这样的数千行代码:
def __len__(*args,**kwargs):
return getattr(args[0],"__len__")(*args,**kwargs)
我了解 __getattr__
和 __getattribute__
@H_502_19@ 之间的区别
覆盖 __getattribute__
而不是 __getattr__
是手头的问题。
解决方法
__getattribute__
已经完成了您真正要求的操作 - 覆盖 __getattribute__
是您处理 .
运算符的所有用法所需的全部内容。 (严格来说,如果 __getattr__
失败,Python 将回退到 __getattribute__
,但只要不实现 __getattr__
,您就不必担心。)
您说您希望“每当 .
在源代码中使用时”调用您的运算符,但是 len
、+
以及您担心的所有其他事情 不要使用 .
。 .
、len(obj)
或 obj + 5
中没有 2 + obj
。
大多数魔术方法查找不使用属性访问。如果您实际查找 yourobj.__len__
或 yourobj.__add__
,将通过属性访问,并且您的 __getattribute__
将被调用,但是当 Python 查找实现语言功能的魔术方法时,它会直接搜索对象类型的 MRO。不涉及 .
运算符。
没有办法覆盖魔术方法查找。这是一个没有覆盖钩子的硬编码过程。您可以做的最接近的事情是覆盖单个魔术方法以委托给 __getattribute__
,但这与覆盖魔术方法查找(或覆盖 .
)不同,并且很容易获得无限递归错误方式。
如果您真正想要做的只是避免重复的单个魔术方法覆盖,您可以将它们放在类装饰器或 mixin 中。