我有一个类,它的方法可能是自动生成的,也可能不是自动生成的。我希望能够从子类调用这些方法,但不知道如何实现。
请看下面的代码:
class Upgrader:
max = 99
def _upgrade_no_op(self, db):
if self.version < Upgrader.max:
self.version += 1
def __getattribute__(self, key):
try:
return super().__getattribute__(key)
except AttributeError:
if key.startswith("upgrade_v"):
nr = int(key[9:])
if 0 <= nr < self.max:
return self._upgrade_no_op
raise
class Foo(Upgrader):
version = 1
def upgrade_v2(self):
# None of these work:
# Upgrade.upgrade_v2(self)
# super().upgrade_v2()
# super(Foo,self).upgrade_v2()
# x = Upgrade.__getattribute__(self, "upgrade_v2"); # bound to Foo.upgrade_v2
breakpoint()
Foo().upgrade_v2()
在我的实际代码中,在Foo
和Upgrader
之间还有一两个其他类,但您应该明白我的意思。
有没有办法做到这一点,最好不用编写我自己的super()
版本?
1条答案
按热度按时间jgwigjjp1#
你有几个问题:
return super().__getattribute__(key)
在超类(通常为object
)上查找__getattribute__
,但是key
的实际查找是普通查找,它没有绕过MRO中的类,它只是从MRO中的 * 第一 * 类(self
实际上是其示例的类)重新开始查找。它会跳过MRO中前面的类,执行一次查找,然后就完成了。这是当属性存在并且从类的方法外部调用时所需要的(Foo().update_v2()
的 * initial * 调用首先通过Updater.__getattribute__
来查找Foo.update_v2
),但是当Foo.update_v2
试图调用update_v2
的"可能不存在"父版本时,即使当你设法调用父__getattribute__
(例如,通过直接调用Upgrader.__getattribute__(self, "upgrade_v2")()
)时,它也只是再次给你Foo.update_v2
,导致无限递归。super()
绕过了许多动态查找机制;在MRO中调用类之后,它只会找到与每个类关联的具有适当名称的内容,但不会尝试在每个类上调用__getattr__
或__getattribute__
(这会大大降低速度,并使代码复杂化)。让它动态地截取 * 所有 * 查找,以便 * 在查找期间 * 将内容插入继承链的中间,这充其量是某种真正强烈的代码味道。在99.99%以上的情况下,您根本不需要动态查找处理,在极少数情况下,您几乎总是需要
__getattr__
(只有在无法以任何其他方式找到名称时才调用它),而不是__getattribute__
(无条件调用它)。但是,这两种特殊方法都不能以您希望的方式与
super()
触发的查找一起工作,因此,如果您真的 * 需要 * 类似的东西,您将不得不手动重新实现super()
正在做的事情,但还要添加动态查找特殊方法支持(手动检查示例的MRO,从每个类中寻找您需要的内容,如果缺少,手动调用__getattr__
/__getattribute__
来查看它是否可以生成它,如果也失败,则继续下一个类)。不要试图去做。我强烈怀疑您这里有an XY problem。您尝试做的东西本质上是脆弱的,只会让 * 任何 *在
Foo.upgrade_v2
中的super().upgrade_v2()
实际上可能在某个类中找到有用的函数,该函数与Foo
一起被某个假设的第三个类继承,该第三个类涉及菱形继承模式,该模式返回到Upgrader
。这已经够复杂的了,现在您要添加__getattribute__
(它会减慢类示例的 * 每次 * 使用,并且即使没有任何类型的继承,它本身也有很多陷阱);这不是个好主意。如果像本例中那样,您有一个应该存在的固定方法集,那么只需预先生成它们,并且完全避免使用
__getattribute__
:在线试用!
您的代码将工作,运行速度显著加快(* every * 属性和方法查找中不涉及Python级别的函数调用),而且它不会让维护人员发疯地试图弄清楚您在做什么。