我有下面的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,以便可以从decorator函数中访问Bar类(或其他类)?
我尝试过在deco_with_args函数上使用deco_with_args,但没有成功。
发布于 2019-03-17 15:39:22
对于您的问题有两种解释--如果您需要在调用示例中名为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语句之前运行。如果您的装饰器会影响类体本身内执行的其他元素,那么您所需要做的就是将这些元素包装起来,以保证延迟执行。
发布于 2019-03-17 14:36:50
@classmethod对装饰器没有任何帮助,因为它不是通过类或实例调用的。classmethod是一个描述符,描述符只对属性访问生效。换句话说,只有当装饰师被称为@Bar.deco_with_args('baz')时才会有帮助。
下一个问题是,在执行装饰器时,类还不存在。Python在创建类之前执行函数体中的所有代码。因此,不可能在deco_with_args或decorator中访问该类。
发布于 2019-03-17 14:33:19
您可以使用描述符协议捕获对方法的调用,并动态添加类作为参数:
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__,然后对__get__返回的函数调用__call__。
注意,当您只执行Bar.some_function时,也会调用Bar.some_function,这是@property之类的装饰器中使用的。
请注意,在使用classmethod时,不应该将第一个参数命名为self,因为它令人困惑(它会使人们认为第一个参数是实例而不是类对象/类型)。
https://stackoverflow.com/questions/55207813
复制相似问题