gpt4 book ai didi

Python 类继承 - 幽灵般的 Action

转载 作者:太空宇宙 更新时间:2023-11-04 01:10:51 24 4
gpt4 key购买 nike

我观察到类继承有一个奇怪的效果。对于我正在从事的项目,我正在创建一个类来充当另一个模块类的包装器。

我正在使用第 3 方 aeidon 模块(用于处理字幕文件),但问题可能不太具体。

这是您通常使用该模块的方式...

project = aeidon.Project()
project.open_main(path)

这里是使用中的'wrapper'类的例子(当然真正的类有很多方法):

class Wrapper(aeidon.Project):
pass

project = Wrapper()
project.open_main(path)

上述代码在执行时引发了一个AttributeError。但是,以下工作与我最初预期的一样:

junk = aeidon.Project()
project = Wrapper()
project.open_main(path)

我把这个问题命名为 spooky action at a distance 因为我怀疑它涉及环境中的全局变量/对象,但我不知道。

我最终使用组合来解决这个问题(即 self.project = aeidon.Project()),但我仍然对此感到好奇。谁能解释一下这是怎么回事?

这是回溯:

---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-5-fe548abd7ad0> in <module>()
----> 1 project.open_main(path)

/usr/lib/python3/dist-packages/aeidon/deco.py in wrapper(*args, **kwargs)
208 def wrapper(*args, **kwargs):
209 frozen = args[0].freeze_notify()
--> 210 try: return function(*args, **kwargs)
211 finally: args[0].thaw_notify(frozen)
212 return wrapper

/usr/lib/python3/dist-packages/aeidon/agents/open.py in open_main(self, path, encoding)
161 format = aeidon.util.detect_format(path, encoding)
162 self.main_file = aeidon.files.new(format, path, encoding)
--> 163 subtitles = self._read_file(self.main_file)
164 self.subtitles, sort_count = self._sort_subtitles(subtitles)
165 self.set_framerate(self.framerate, register=None)

/usr/lib/python3/dist-packages/aeidon/project.py in __getattr__(self, name)
116 return self._delegations[name]
117 except KeyError:
--> 118 raise AttributeError
119
120 def __init__(self, framerate=None, undo_limit=None):

AttributeError:

无论是否调用 Project 的 __init__(),我都试过了。显然这不是正常情况下真正应该做的事情,我只是很困惑为什么 Wrapper() 只能在创建垃圾 aeidon.Project() 后才能按预期运行。

最佳答案

aedion.project module做两件事:

  • 它将 aedion.agents 包中的类的方法添加到类中,以便文档生成器在提取文档字符串和其他信息时包含这些方法,使用 ProjectMeta metaclass :

    class ProjectMeta(type):

    """
    Project metaclass with delegated methods added.
    Public methods are added to the class dictionary during :meth:`__new__`
    in order to fool Sphinx (and perhaps other API documentation generators)
    into thinking that the resulting instantiated class actually contains those
    methods, which it does not since the methods are removed during
    :meth:`Project.__init__`.
    """

    但是,如果使用这些方法,则不会正确绑定(bind)。

  • Project.__init__ 方法调用 Project._init_delegations() .此方法删除类中的委托(delegate)方法:

    # Remove class-level function added by ProjectMeta.
    if hasattr(self.__class__, attr_name):
    delattr(self.__class__, attr_name)

    注意这里使用了self.__class__。这是必需的,因为如果在类中找到该方法,Python 将不会通过 __getattr__ Hook 查找委托(delegate)方法。

    委托(delegate)方法绑定(bind)到专用代理实例,因此实际上是委托(delegate)给该代理:

    agent = getattr(aeidon.agents, agent_class_name)(self)
    # ...
    attr_value = getattr(agent, attr_name)
    # ...
    self._delegations[attr_name] = attr_value

当您围绕此类创建包装器时,删除 步骤会失败。 self.__class__ 是您的包装器,而不是基础 Project 类。因此方法绑定(bind)不正确;元类提供的方法被绑定(bind),并且永远不会调用 __getattr__ Hook 来查找委托(delegate)的方法:

>>> import aeidon
>>> class Wrapper(aeidon.Project): pass
...
>>> wrapper = Wrapper()
>>> wrapper.open_main
<bound method Wrapper.open_main of <__main__.Wrapper object at 0x1106313a8>>
>>> wrapper.open_main.__self__
<__main__.Wrapper object at 0x1106313a8>
>>> wrapper._delegations['open_main']
<bound method OpenAgent.open_main of <aeidon.agents.open.OpenAgent object at 0x11057e780>>
>>> wrapper._delegations['open_main'].__self__
<aeidon.agents.open.OpenAgent object at 0x11057e780>

因为 Project 上的 open_main 方法仍然存在:

>>> Project.open_main
<function OpenAgent.open_main at 0x110602bf8>

一旦创建了 Project 的实例,这些方法就会从类中删除:

>>> Project()
<aeidon.project.Project object at 0x1106317c8>
>>> Project.open_main
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'Project' has no attribute 'open_main'

并且您的包装器将开始工作,因为现在已找到委托(delegate)的 open_main:

>>> wrapper.open_main
<bound method OpenAgent.open_main of <aeidon.agents.open.OpenAgent object at 0x11057e780>>
>>> wrapper.open_main.__self__
<aeidon.agents.open.OpenAgent object at 0x11057e780>

您的包装器必须自己进行删除:

class Wrapper(aeidon.Project):
def __init__(self):
super().__init__()
for name in self._delegations:
if hasattr(aeidon.Project, name):
delattr(aeidon.Project, name)

请注意,如果 aedion 维护者将 self.__class__ 替换为 __class__(没有 self)他们的代码您的子类方法仍然有效,而无需再次手动清理类。这是因为在 Python 3 中,__class__ 引用 in methods 是一个自动闭包变量,指向定义方法的类。对于 Project._init_delegations(),那将是 Project。或许您可以就此提交错误报告。

关于Python 类继承 - 幽灵般的 Action ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27720339/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com