慕码人2483693
我可以想到一个解决方案,它并不完美,但可能是一个开始。我们可以通过从装饰类继承的类中的__getattribute__和捕获实例属性访问__setattribute__:import redunder_pattern = re.compile("__.*__")protected_pattern = re.compile("_.*")def is_hidden(attr_name): return dunder_pattern.match(attr_name) or protected_pattern.match(attr_name)def attach_proxy(function=None): function = function or (lambda *a: None) def decorator(decorated_class): class Proxy(decorated_class): def __init__(self, *args, **kwargs): function("init", args, kwargs) super().__init__(*args, **kwargs) def __getattribute__(self, name): if not is_hidden(name): function("acces", name) return object.__getattribute__(self, name) def __getattr__(self, name): if not is_hidden(name): function("acces*", name) return object.__getattr__(self, name) def __setattribute__(self, name, value): if not is_hidden(name): function("set", name, value) return object.__setattribute__(self, name, value) def __setattr__(self, name, value): if not is_hidden(name): function("set*", name, value) return object.__setattr__(self, name, value) return Proxy return decorator然后你可以用它来装饰你的班级:@attach_proxy(print)class A: x = 1 def __init__(self, y, msg="hello"): self.y = y @classmethod def foo(cls): print(cls.x) def bar(self): print(self.y)这将导致以下结果:>>> a = A(10, msg="test")init (10,) {'msg': 'test'}set* y 10>>> a.bar()acces baracces y10>>> a.foo() # access to x is not capturedacces foo1>>> y = a.yacces y>>> x = A.x # access to x is not captured>>> a.y = 3e5set* y 300000.0问题:不会捕获类属性访问(为此需要一个元类,但我看不到即时执行的方法)。TypeA是隐藏的(在 type 后面Proxy),这可能更容易解决:>>> A__main__.attach_proxy.<locals>.decorator.<locals>.Proxy另一方面,这不一定是问题,因为这会按预期工作:>>> a = A(10, msg="test")>>> isinstance(a, A)True编辑请注意,我不会将实例传递给function调用,但这实际上是一个好主意,将调用替换为function("acces", name)to function("acces", self, name)。这将允许与您的装饰者一起制作更多有趣的东西。