我有以下Python元类,它们为每个类添加了deco_with_args
装饰器:
def deco_with_args(baz):
def decorator(func):
...
return func
return decorator
class Foo(type):
def __prepare__(name, bases):
return {'deco_with_args': deco_with_args}
这允许我像这样使用装饰器:
class Bar(metaclass=Foo):
@deco_with_args('baz')
def some_function(self):
...
如何使deco_with_args
装饰器的行为类似于@classmethod
,以便可以从Bar
函数中访问decorator
类(或其他任何类)?
我尝试在@classmethod
函数上使用deco_with_args
时没有运气。
答案 0 :(得分:1)
259999999999999786837179271969944
对装饰器没有任何帮助,因为它不是通过类或实例调用的。 @classmethod
是descriptor,并且描述符仅对属性访问有效。换句话说,仅当装饰器的调用方式为classmethod
时,它才会有所帮助。
下一个问题是,在执行装饰器时,类还不存在。在创建类之前,Python执行 函数体中的所有代码。因此,无法访问@Bar.deco_with_args('baz')
或deco_with_args
中的类。
答案 1 :(得分:1)
您的问题有两种解释-调用示例中名为cls
的函数时,是否需要decorator
可用(即,需要装饰的方法才能成为类方法),它会本身就可以转换为类方法:
def deco_with_args(baz):
def decorator(func):
...
return classmethod(func)
return decorator
第二个是如果您需要cls
在调用deco_with_args
本身时,在创建修饰函数本身时,在类创建时可用。现在被列为已接受的答案列出了以下直接问题:运行类主体时该类尚不存在,因此,无法在分析该类结束时正文,您可以拥有类本身已知的方法。
但是,不同于该答案试图暗示的是,这不是真正的交易。您要做的就是在类创建过程结束时懒惰地运行装饰器代码(需要cls
的代码)。您已经有一个元类设置,因此,只需在装饰代码周围添加另一个可调用层,就可以做到这一点几乎是简单的:
def deco_with_args(baz):
def outter_decorator(func):
def decorator(cls):
# Code that needs cls at class creation time goes here
...
return func
return decorator
outter_decorator._deco_with_args = True
return outter_decorator
class Foo(type):
def __prepare__(name, bases):
return {'deco_with_args': deco_with_args}
def __init__(cls, cls_name, bases, namespace, **kwds):
for name, method in cls.__dict__.items():
if getattr(method, '_deco_with_args', False):
cls.__dict__[name] = method(cls)
super().__init__(cls_name, bases, namespace, **kwds)
当然,这将在类主体执行完成之后但在class
之后的任何其他Python语句之前运行。
如果您的装饰器会影响在类主体本身内部执行的其他元素,那么您所需要做的就是将它们包装起来以保证也可以进行延迟执行。
答案 2 :(得分:0)
您可以使用descriptor protocol捕获对方法的调用,并动态添加类作为参数:
def another_classmethod(baz):
class decorator:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
def new_call(*args, **kwargs):
print(baz, self.func(owner, *args, **kwargs))
return new_call
return decorator
class Bar():
@another_classmethod('baz')
def some_function(cls):
return f"test {cls.__name__}"
Bar.some_function()
此打印:
baz test Bar
此处的主要“技巧”是,调用Bar.some_function()
时的协议是先调用__get__
,然后依次调用__call__
返回的函数__get__
。
请注意,__get__
也会在您执行Bar.some_function
时被调用,这就是@property
这样的修饰符所使用的。
一句话,在使用classmethod时,您不应该为您的第一个参数self
命名,因为它很容易混淆(这会使人们认为第一个参数是实例而不是类对象/类型)。