如何用子类内部状态包装所有 python 超类方法?
Posted
技术标签:
【中文标题】如何用子类内部状态包装所有 python 超类方法?【英文标题】:How to wrap all python superclass methods with subclass internal state? 【发布时间】:2021-09-25 01:27:59 【问题描述】:我知道this question,但我认为它不包括我的情况。
我有一个带有许多方法的外部 Dummy 类,所有方法都使用实例属性。我希望能够将其作为参数传递,而不是使用此实例属性。我的解决方案是保留一组假人,并在必要时使用具有适当属性的人。
class Dummy:
def __init__(self, prefix="dum"):
self.prefix = prefix
def toto(self):
return f"self.prefix_toto"
def titi(self):
return f"self.prefix_titi"
def tata(self):
return f"self.prefix_tata"
class DynamicDummy:
def __init__(self):
self.dummies =
def _get_dummy(self, prefix):
dummy = self.dummies.get(prefix)
if dummy is None:
dummy = Dummy(prefix)
self.dummies[prefix] = dummy
return dummy
def toto(self, prefix):
dummy = self._get_dummy(prefix)
return dummy.toto()
def titi(self, prefix):
dummy = self._get_dummy(prefix)
return dummy.titi()
def tata(self, prefix):
dummy = self._get_dummy(prefix)
return dummy.tata()
问题是,有超过 3 种方法,我希望它是自动的,这样我就不必在每次 Dummy
发生变化时更改我的 DynamicDummy
。我以前从未使用过元类,所以也许它们是解决方案,但我不能让它们与 dummies
字典一起使用,这是一个实例属性。
我愿意寻求一种使其自动化的解决方案,但也愿意为“动态性”问题提供另一种解决方案。
【问题讨论】:
为什么没有像get_name(self, name, prefix)
这样的单一方法,而是像get_name('toto', 'some_prefix')
那样调用它,而不是使用多个相同的方法?这适用于两个课程。
你也可以看看customization of attribute access
因为这是一个简化的例子,实际的方法是不一样的
即使在这种情况下,我仍然建议查看属性访问的自定义。
【参考方案1】:
按照@buran 的建议,我修改了__getattribute__
方法。
class SmartDynamicDummy(Dummy):
def __init__(self):
self.dummies =
def _get_dummy(self, prefix):
dummy = self.dummies.get(prefix)
if dummy is None:
dummy = Dummy(prefix)
self.dummies[prefix] = dummy
return dummy
def _wrapper(self, func, func_name):
@wraps(func)
def wrapped(*args, **kwargs):
args = list(args)
prefix = args.pop(0)
args = tuple(args)
dummy = self._get_dummy(prefix)
dummy_func = getattr(dummy, func_name)
return dummy_func(*args, **kwargs)
return wrapped
def __getattribute__(self, name):
attr = super(SmartDynamicDummy, self).__getattribute__(name)
if isinstance(attr, types.MethodType) and not name.startswith('_'):
# attr.__name__ and name can be different if attr is a decorated function
attr = self._wrapper(attr, name)
return attr
【讨论】:
策略很好 - 但要让行为保持你提到的你不想要的行为有点复杂:这种设计仍然需要“dummy_collection”属性。【参考方案2】:如果您在拨打电话之前更改了目标实例上的 prefix
属性,它将保持更改并正常工作 - 不需要为 prefix
设置具有不同值的实例集合。一种情况是值可能会在中途更改,如果您的应用程序使用线程并行化 - 在这种情况下,可以在 DynamicDummy
上调用其他方法之一,在另一个调用结束之前需要不同的“前缀”。这就是Locks
可以解决的问题。
元类在这里并没有真正的作用。当然,可以设计一个涉及元类的复杂事物,但我们只是处理普通的属性设置;
所以,换句话说:如果你的程序没有并行运行,一旦你输入SmartDummy.toto
,并且这会调用一个 Dummy 实例.toto()
,就不可能调用同一个 Dummy 实例的另一个方法,直到两个调用都已解决 - 因此您只需在 SmartDummy.toto
中设置所需的值,然后在关联的虚拟实例中调用该方法。
如果您的代码确实并行运行,但使用multiprocessing
模型:同样适用:在每个进程中,SmartDummy
的实例就像在串行程序中一样运行,并且没有任何外部事物可以更改 @987654330 SmartDummy.toto
之前的 @ 属性解析。
如果您的代码是并行的,但使用asyncio
,如果toto
、tata
等方法本身是异步方法并且将控制权交给异步循环,则可以进行中途更改。如果它们未声明为“异步”,则保证不会并行运行可能修改属性值的代码(甚至不能通过其他函数或方法 Dummy.toto
调用:如果它不是“异步”,则不能让循环执行。它甚至可以安排新任务的运行,但只有当您将执行返回到主循环时才会触及这些任务,并且来自发生在函数本身末尾的非异步函数。
所以,我们回到:只需保存属性值、拨打电话并恢复它。为多线程情况添加 Lock 规定,您就可以了。
假设您无权访问Dummy
代码,并且DynamicDummy
的每个实例都关联一个Dummy
实例,我们可以在DynamicDummy
实例上创建一个锁。如果所有DynamicDummy
s 都拥有Dummy
的一个实例,那么锁就必须是一个类属性。
要以透明的方式调用包装的方法,您得到的设计也很好:我只是将其更改为使用__getattr__
,因为它更简单:
import threading
class DymamicDummy:
def __init__(self, ...):
...
self.dummy = Dummy()
self.lock = threading.Lock()
def _dispatcher(self, method):
# the same thign you found your design:
# 'prefix' is always the first positional argument. Change it to a named parameter if desired.
def wrapper(prefix, *args, **kwargs):
with self.lock:
original_prefix = self.dummy.prefix
self.dummy.prefix = prefix
try:
result = method(*args, **kwargs)
finally:
self.dummy.prefix =self.dummy.prefix
return result
return wrapper
def __getattr__(self, method_name):
# using __getattr__ instead of __getattribute__:
# this only gets called for methods or attributes that do not exist
# on the current instance anyway: so no need to further testing,
# just extract the value from Dummy.
method = getattr(self.dummy, method_name)
if not callable(method):
return method
return _dispatcher(method)
【讨论】:
感谢您的回答。我真正的课不是那么简单。我的“前缀”实际上是一个时区,每个 Dummy 实例都有一个数据帧,该数据帧根据时区进行不同的解释。仅仅改变时区是不够的。以上是关于如何用子类内部状态包装所有 python 超类方法?的主要内容,如果未能解决你的问题,请参考以下文章