首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python - Metaclass装饰器-如何使用@classmethod

Python - Metaclass装饰器-如何使用@classmethod
EN

Stack Overflow用户
提问于 2019-03-17 13:56:47
回答 3查看 1.3K关注 0票数 3

我有下面的Python,它为每个类添加了一个deco_with_args装饰器:

代码语言:javascript
复制
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}

这样我就可以像这样使用装饰器了:

代码语言:javascript
复制
class Bar(metaclass=Foo):
    @deco_with_args('baz')
    def some_function(self):
        ...

如何使deco_with_args装饰器的行为像@classmethod,以便可以从decorator函数中访问Bar类(或其他类)?

我尝试过在deco_with_args函数上使用deco_with_args,但没有成功。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-03-17 15:39:22

对于您的问题有两种解释--如果您需要在调用示例中名为cls的函数(即,您需要您的修饰方法才能成为类方法)时使用decorator,那么它本身就可以转换为一个类方法:

代码语言:javascript
复制
def deco_with_args(baz):
    def decorator(func):
        ...
        return classmethod(func)
    return decorator

第二个问题是,当调用cls本身时,当创建修饰函数本身时,在类创建时,如果您需要deco_with_args可用的话。现在列出的被接受的答案列出了直截了当的问题:当类主体运行时,类还不存在,因此,在解析类体结束时,您不可能有知道类本身的方法。

然而,不像这个答案试图暗示,这不是一个真正的交易。您所要做的就是在类创建过程结束时懒洋洋地运行您的装饰代码(需要cls的代码)。您已经有了元类设置,因此,只需在装饰器代码周围添加另一个可调用层,这样做几乎很简单:

代码语言:javascript
复制
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语句之前运行。如果您的装饰器会影响类体本身内执行的其他元素,那么您所需要做的就是将这些元素包装起来,以保证延迟执行。

票数 4
EN

Stack Overflow用户

发布于 2019-03-17 14:36:50

@classmethod对装饰器没有任何帮助,因为它不是通过类或实例调用的。classmethod是一个描述符,描述符只对属性访问生效。换句话说,只有当装饰师被称为@Bar.deco_with_args('baz')时才会有帮助。

下一个问题是,在执行装饰器时,类还不存在。Python在创建类之前执行函数体中的所有代码。因此,不可能在deco_with_argsdecorator中访问该类。

票数 2
EN

Stack Overflow用户

发布于 2019-03-17 14:33:19

您可以使用描述符协议捕获对方法的调用,并动态添加类作为参数:

代码语言:javascript
复制
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()

这些指纹:

代码语言:javascript
复制
baz test Bar

这里的主要“技巧”是,当调用Bar.some_function()时,协议首先调用__get__,然后对__get__返回的函数调用__call__

注意,当您只执行Bar.some_function时,也会调用Bar.some_function,这是@property之类的装饰器中使用的。

请注意,在使用classmethod时,不应该将第一个参数命名为self,因为它令人困惑(它会使人们认为第一个参数是实例而不是类对象/类型)。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55207813

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档