如何访问 python 实例方法对象的捕获参数/闭包?
How to access captured arguments / closure of python instance method objects?
这个想法是以非泄漏/自动清理的方式实现观察者模式。因此,当关联对象被 gc 清理时,实例方法对象应该被删除。
我最初的想法是只存储对带有终结器的实例方法对象的弱引用以调用清理例程。
class Observeable:
def __init__(self):
self._callbacks: list = []
self._dirty = False
def add_callback(self, callback):
finalize(callback, self._set_dirty)
self._callbacks.append(ref(callback))
def trigger_callbacks(self, *args, **kwargs):
if self._dirty:
self._cleanup_callbacks()
for callback in self._callbacks:
callback()(*args, **kwargs)
def _set_dirty(self):
self._dirty = True
def _cleanup_callbacks(self):
for callback in self._callbacks:
if not callback():
self._callbacks.remove(callback)
self._dirty = False
然而事实证明,该方法在概念上存在缺陷,因为实例方法对象的生命周期未绑定到关联对象。
这让我想到从实例方法对象的闭包中提取 self 参数并绑定到它的生命周期。这当然可以通过将第二个参数传递给 add_callback 来完成,但是从闭包中提取它会更干净。
由于我找不到有关闭包如何存储在函数对象中的任何有用信息,因此我有几个问题要问你们。
- 我的想法对吗?存储实例方法对象将防止自动清理后面的对象,对吗?
- 是否可以从函数对象中提取 self 参数/一般闭包?
- 函数对象有更“正式”的名称吗? python 文档也只是这样称呼它,但是似乎没有关于其更底层实现的有用信息。
我希望我的问题/问题很清楚,提前感谢您的帮助!
@fountainhead 主要用他的评论回答了这个问题,所以我会很快总结一下。
方法中的函数对象正式称为“实例方法对象”(https://docs.python.org/3/reference/datamodel.html,搜索方法对象)
因此1.它会阻止关联对象被gc清理。
2.可以通过__self__
访问,3.上面已经回答了
为了让 Observable Class 像我预期的那样工作,我基本上将函数名和一个 weakref 保存到 __self__
,然后用 getattr()
调用它。很有魅力!
from weakref import finalize, ref
from functools import partial
class Observeable:
def __init__(self):
self._methods: list = []
self._functions: list = []
self._dirty = False
def add_callback_methode(self, callback):
"""
Adds a callback from a method.
The callback will automatically be cleanup,
when its associated objects gets out of scope,
therefor also not preventing the gc from cleaning it up.
"""
finalize(callback.__self__, self._set_dirty)
self._methods.append((callback.__name__, ref(callback.__self__)))
def trigger_callbacks(self, *args, **kwargs):
"""Forwards the given args and kwargs to all callbacks"""
if self._dirty:
self._cleanup_callbacks()
for callback in self._methods:
getattr(callback[1](), callback[0])(*args, **kwargs)
def _set_dirty(self):
self._dirty = True
def _cleanup_callbacks(self):
for callback in self._methods:
if not callback[1]():
self._methods.remove(callback)
self._dirty = False
这个想法是以非泄漏/自动清理的方式实现观察者模式。因此,当关联对象被 gc 清理时,实例方法对象应该被删除。
我最初的想法是只存储对带有终结器的实例方法对象的弱引用以调用清理例程。
class Observeable:
def __init__(self):
self._callbacks: list = []
self._dirty = False
def add_callback(self, callback):
finalize(callback, self._set_dirty)
self._callbacks.append(ref(callback))
def trigger_callbacks(self, *args, **kwargs):
if self._dirty:
self._cleanup_callbacks()
for callback in self._callbacks:
callback()(*args, **kwargs)
def _set_dirty(self):
self._dirty = True
def _cleanup_callbacks(self):
for callback in self._callbacks:
if not callback():
self._callbacks.remove(callback)
self._dirty = False
然而事实证明,该方法在概念上存在缺陷,因为实例方法对象的生命周期未绑定到关联对象。
这让我想到从实例方法对象的闭包中提取 self 参数并绑定到它的生命周期。这当然可以通过将第二个参数传递给 add_callback 来完成,但是从闭包中提取它会更干净。
由于我找不到有关闭包如何存储在函数对象中的任何有用信息,因此我有几个问题要问你们。
- 我的想法对吗?存储实例方法对象将防止自动清理后面的对象,对吗?
- 是否可以从函数对象中提取 self 参数/一般闭包?
- 函数对象有更“正式”的名称吗? python 文档也只是这样称呼它,但是似乎没有关于其更底层实现的有用信息。
我希望我的问题/问题很清楚,提前感谢您的帮助!
@fountainhead 主要用他的评论回答了这个问题,所以我会很快总结一下。
方法中的函数对象正式称为“实例方法对象”(https://docs.python.org/3/reference/datamodel.html,搜索方法对象)
因此1.它会阻止关联对象被gc清理。
2.可以通过__self__
访问,3.上面已经回答了
为了让 Observable Class 像我预期的那样工作,我基本上将函数名和一个 weakref 保存到 __self__
,然后用 getattr()
调用它。很有魅力!
from weakref import finalize, ref
from functools import partial
class Observeable:
def __init__(self):
self._methods: list = []
self._functions: list = []
self._dirty = False
def add_callback_methode(self, callback):
"""
Adds a callback from a method.
The callback will automatically be cleanup,
when its associated objects gets out of scope,
therefor also not preventing the gc from cleaning it up.
"""
finalize(callback.__self__, self._set_dirty)
self._methods.append((callback.__name__, ref(callback.__self__)))
def trigger_callbacks(self, *args, **kwargs):
"""Forwards the given args and kwargs to all callbacks"""
if self._dirty:
self._cleanup_callbacks()
for callback in self._methods:
getattr(callback[1](), callback[0])(*args, **kwargs)
def _set_dirty(self):
self._dirty = True
def _cleanup_callbacks(self):
for callback in self._methods:
if not callback[1]():
self._methods.remove(callback)
self._dirty = False