0

假设我们正在使用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__.
以下等价于x = robby.length

x = Rectangle.__getattribute__(robby, "length")    

但是,点运算符并不总是实现为__getattribute__.

Python 有“魔术方法魔术方法是名称以两个下划线字符开头和结尾的任何方法
。是魔术方法的一个例子。
__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
然后我尝试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 的点运算符。
什么是一种可读、干净和一致的方法的例子?

一致,我的意思是我们的自定义点运算符在源代码中使用时被调用,.无论该方法是否是魔术方法。

我不愿意在阳光下手动输入每一个魔法方法。
我不想看到成千上万行代码,如下所示:

def __len__(*args, **kwargs):
    return getattr(args[0], "__len__")(*args, **kwargs)

__getattr__我理解和__getattribute__
Overriding__getattribute__而不是__getattr__之间的区别不是手头的问题。

4

1 回答 1

2

__getattribute__已经完成了您真正要求的事情 - 覆盖__getattribute__是您处理运算符的所有使用所需的全部内容.。(严格来说,Python 会回退到__getattr__if__getattribute__失败,但只要你不实现,你就不必担心这个__getattr__。)


你说你希望你的操作符被称为“每当.在源代码中使用时”,但是len,+和你担心的所有其他事情不要使用.. 、或.中没有。len(obj)obj + 52 + obj

大多数魔术方法查找不使用属性访问。如果您实际查找yourobj.__len__or yourobj.__add__,那将通过属性访问,并且您__getattribute__将被调用,但是当 Python 查找实现语言功能的魔术方法时,它会直接搜索对象类型的 MRO。.运营商不参与。

没有办法覆盖魔术方法查找。这是一个没有覆盖挂钩的硬编码过程。您可以做的最接近的事情是覆盖单个魔术方法以委托给__getattribute__,但这与覆盖魔术方法查找(或覆盖.)不同,并且很容易获得无限递归错误。

如果您真正想要做的只是避免重复的单个魔术方法覆盖,您可以将它们放在类装饰器或 mixin 中。

于 2021-01-02T23:57:20.587 回答