我想创建一个上下文管理器,该上下文管理器将拦截给定类或此类的子类中的任何对象实例,并对该对象执行某些操作。
我sudo代码看起来可能像这样:
with my_context_watchdog(my_callback, BaseClass):
obj_1 = BaseClass(name) # launch callback
obj_2 = SubClass(name) # launch callback
obj_3 = RandomClass(name) # does not launch callback
def my_callback(new_obj):
print("new object name:", new_obj.name)
反正我可以在Python中做类似的事情吗?
最好是仅在对象完全实例化后 后才执行回调。
重要提示::我无法修改任何对象类,它们在我没有任何控件的库中。我只想在实例化该库的某些类时引发一个回调。
也许contextmanager不是最好的主意,我对任何其他解决方案都持开放态度。
答案 0 :(得分:2)
上下文管理器实际上在with
块中看不到代码。如果您看到一个库在做这样的“魔术”操作,通常在后台某处有一个隐藏的全局变量,并且with
块内部调用的函数会与上下文管理器积极配合。
也就是说,您有两个选择。一个有意义,一个非常丑陋。
有意义的一个:
# somewhere in the context manager:
class ContextManagerReturnVal:
def instantiate(self, cls, *args, **kwargs):
obj = cls(*args, **kwargs)
if issubclass(cls, self.base_class):
self.callback(cls, *args, **kwargs)
return obj
with watchdog(callback, BaseClass) as instantiate:
obj1 = instantiate(BaseClass, name)
obj2 = instantiate(SubClass, name)
丑陋的是猴子在修补BaseClass
。您在评论中说,您不能修改 BaseClass
-取决于您的意思。如果您无法修改源代码,则仍可以在运行时修改该类,例如:Monkey patching a class in another module in Python
您必须存储原始的__init__
方法,并在上下文管理器退出后返回它...当然,不建议这样做,因为您基本上是将手指放在不应该使用的位置,并且可能引入难以理解的方法。查找错误。
除此之外,恐怕无法完成。
答案 1 :(得分:1)
我可以看到您可以轻松实现一个稍微不同的上下文管理器:
with my_context_watchdog(some_instance):
pass
...但是这可能和检查器功能一样好,而不是您想要的功能。上下文管理器的神奇之处在于__enter__方法,因此在那里可以发生很多事情,而没有任何种类的大规模重载。可能有一种voerride __init__方法
您还可以创建一些可以区分类名称的构造函数,并考虑将其应用于__new__方法:
def new(cls):
if cls.__name__ == 'BaseClass':
pass
# do something
else:
pass
# do a different thing