装饰器不是让函数看起来漂亮的东西。它是一个可调用对象(不仅是函数),执行一些任意操作,并返回一个替换对象。
在这种情况下,您的装饰器应该在某处的字典中存储对函数对象的引用。问题是,在创建函数之前,您将无法引用定义函数的类,这在装饰器运行之后发生。您可以通过存储类名和函数名来避免这种情况。
这里的最后一步是将函数对象正确绑定到正确对象上的方法。这是get_func_by_decorated_name 可以为您做的事情。
总之,你可以这样写:
decorated_func_dict = {}
def func_name(cls_name, func_name):
def decorator(func):
decorated_func_dict.setdefault(cls_name, {})[func_name] = func
return func
return decorator
class Parent:
def get_func_by_decorator_name(self, name):
return decorated_func_dict[type(self).__name__][name].__get__(self)
class Child1(Parent):
@func_name("Child1", "Bob")
def bob_func(self, *args):
pass
@func_name("Child1", "Tom")
def func2(self, *args):
pass
class Child2(Parent):
@func_name("Child2", "Bob")
def func_bob2(self, *args):
pass
你确实得到了:
>>> foo.get_func_by_decorator_name("Bob")
<bound method Child1.bob_func of <__main__.Child1 object at 0x000001D58181E070>>
>>> bar.get_func_by_decorator_name("Bob")
<bound method Child2.func_bob2 of <__main__.Child2 object at 0x000001D582041F10>>
另一种方法是给你的函数一个名称属性,然后你可以在__init_subclass__ 和Parent 中聚合到一个映射中。这使您可以使界面更接近您最初的意图:
def func_name(func_name):
def decorator(func):
func.special_name = func_name
return func
return decorator
class Parent:
def __init_subclass__(cls):
cls.decorated_func_dict = {}
for item in cls.__dict__.values():
if hasattr(item, 'special_name'):
cls.decorated_func_dict[item.special_name] = item
del item.special_name # optional
def get_func_by_decorator_name(self, name):
return self.decorated_func_dict[name].__get__(self)
class Child1(Parent):
@func_name("Bob")
def bob_func(self, *args):
pass
@func_name("Tom")
def func2(self, *args):
pass
class Child2(Parent):
@func_name("Bob")
def func_bob2(self, *args):
pass
结果与第一个示例相同。
最简单的方法当然是在创建类之前访问孩子的命名空间,例如使用元类。