为什么我们必须使用__getitem__而不是通常的操作员访问?
class MyDict(dict):
def __getitem__(self, key):
return super()[key]
Run Code Online (Sandbox Code Playgroud)
我们得到了TypeError: 'super' object is not subscriptable.
相反,我们必须使用super().__getitem__(key),但我从未完全理解为什么 - 它究竟是什么阻止了超级操作以允许操作员访问的方式?
标化只是一个例子,我也有同样的问题__getattr__,__init__等等.
本文档试图解释为什么,但我不明白.
Vee*_*rac 20
CPython的错误跟踪器问题805304,"超级实例不支持项目分配", Raymond Hettinger详细解释了感知困难.
这不起作用的原因是由于Python的方法缓存,必须在类上定义这样的方法,而代理方法在运行时找到.
+ if (o->ob_type == &PySuper_Type) {
+ PyObject *result;
+ result = PyObject_CallMethod(o, "__setitem__", "(OO)", key, value);
+ if (result == NULL)
+ return -1;
+ Py_DECREF(result);
+ return 0;
+ }
+
Run Code Online (Sandbox Code Playgroud)
所以显然是可能的.
不过,他总结道
我一直在想这个可以留下来,只记录超级对象只有在显式属性查找时才能发挥作用.
否则,完全修复它需要为每个直接从插槽表调用函数的地方组合Python,然后在插槽为空时使用属性查找添加后续调用.
当谈到像repr(obj)这样的函数时,我认为我们希望超级对象能够识别自身,而不是将调用转发给目标对象的__repr __()方法.
参数似乎是如果__dunder__方法被代理,则要么代理,要么__repr__它们之间存在不一致.super()因此,可能不想代理这样的方法,以免它太接近程序员相当于一个不可思议的山谷.
你所要求的都可以完成,而且很容易。例如:
class dundersuper(super):
def __add__(self, other):
# this works, because the __getattribute__ method of super is over-ridden to search
# through the given object's mro instead of super's.
return self.__add__(other)
super = dundersuper
class MyInt(int):
def __add__(self, other):
return MyInt(super() + other)
i = MyInt(0)
assert type(i + 1) is MyInt
assert i + 1 == MyInt(1)
Run Code Online (Sandbox Code Playgroud)
所以 super 与魔法方法一起工作的原因并不是因为它不可能。原因一定在其他地方。原因之一是这样做会违反 equals ( ) 约定==。除其他标准外,即等于是对称的。这意味着如果a == b为真则b == a也一定为真。这让我们陷入了一个棘手的境地,其中super(self, CurrentClass) == self,但是self != super(self, CurrentClass)例如。
class dundersuper(super):
def __eq__(self, other):
return self.__eq__(other)
super = dundersuper
class A:
def self_is_other(self, other):
return super() == other # a.k.a. object.__eq__(self, other) or self is other
def __eq__(self, other):
"""equal if both of type A"""
return A is type(self) and A is type(other)
class B:
def self_is_other(self, other):
return other == super() # a.k.a object.__eq__(other, super()), ie. False
def __eq__(self, other):
return B is type(self) and B is type(other)
assert A() == A()
a = A()
assert a.self_is_other(a)
assert B() == B()
b = B()
assert b.self_is_other(b) # assertion fails
Run Code Online (Sandbox Code Playgroud)
另一个原因是,一旦 super 完成搜索它给定对象的 mro,它就必须给自己一个提供所请求属性的机会 - super 对象本身仍然是一个对象 - 我们应该能够测试与其他对象的相等性对象,请求字符串表示形式,并内省 super 正在使用的对象和类。如果 dunder 方法在超级对象上可用,但在可变对象表示的对象上不可用,则会产生问题。例如:
class dundersuper(super):
def __add__(self, other):
return self.__add__(other)
def __iadd__(self, other):
return self.__iadd__(other)
super = dundersuper
class MyDoubleList(list):
"""Working, but clunky example."""
def __add__(self, other):
return MyDoubleList(super() + 2 * other)
def __iadd__(self, other):
s = super()
s += 2 * other # can't assign to the result of a function, so we must assign
# the super object to a local variable first
return s
class MyDoubleTuple(tuple):
"""Broken example -- iadd creates infinite recursion"""
def __add__(self, other):
return MyDoubleTuple(super() + 2 * other)
def __iadd__(self, other):
s = super()
s += 2 * other
return s
Run Code Online (Sandbox Code Playgroud)
对于列表示例,该函数__iadd__可以更简单地写为
def __iadd__(self, other):
return super().__iadd__(other)
Run Code Online (Sandbox Code Playgroud)
对于元组示例,我们陷入无限递归,这是因为tuple.__iadd__不存在。因此,当查找__iadd__超级对象的属性时,将检查实际超级对象的__iadd__属性(该属性确实存在)。我们获取该方法并调用它,这会再次启动整个过程。如果我们没有__iadd__在 super 上编写一个方法并使用,super().__iadd__(other)那么这永远不会发生。相反,我们会收到一条关于超级对象没有 attribute 的错误消息__iadd__。有点神秘,但不如无限堆栈跟踪那么神秘。
所以 super 不能与魔法方法一起使用的原因是它产生的问题多于它解决的问题。
| 归档时间: |
|
| 查看次数: |
1099 次 |
| 最近记录: |