用正确的名字装饰一个类

Posted

技术标签:

【中文标题】用正确的名字装饰一个类【英文标题】:Decorating a class with the correct names 【发布时间】:2021-11-21 03:23:24 【问题描述】:

我正在尝试装饰一个班级

def decorate_module(Module):
    class Wrapper(Module):
        def __init__(self, cfg):
            self.c = create_c(**cfg)
            super().__init__()

    return Wrapper

虽然代码有效,但它提供了一个名为“Wrapper”的类,而不是原始模块的名称。在功能装饰方面,这种情况下可以使用@wraps。我在课堂上尝试了同样的事情(通过在 class Wrapper(Module): 上方写它但出现错误。

这样做的正确方法是什么? python 内置装饰器@dataclass 似乎处理得很好。我想知道它是如何工作的。有没有像@wraps 这样的辅助函数?还是我应该手动覆盖__name__?如果是这样,这些“双下划线”属性的完整列表是什么?


(编辑)我最初的目的

给定一个在__init__ 中不接受任何参数的类,我想将其装饰为接受一个参数“cfg”,执行一些操作(create_c),并将其存储为一个属性。

确切地说,我的装饰器实际上将“create_c”作为参数(我在上面的代码中省略了这一点,因为额外的嵌套会使它变得冗长)。然后,我想如下使用它。

@decorate_module(create_fn1)
class Module1:
    ...

@decorate_module(create_fn2)
class Module2:
    ...

【问题讨论】:

通常装饰器修改一个类而不是完全替换它。你的装饰器的实际目的是什么? @nzer0 如果您将一个类子类化并赋予它与父类相同的名称,您究竟期望什么?你觉得ModuleJr 合理吗? 下划线属性列表for i in dir(my_class): print(i) if i.startswith('__') and i.endswith('__') else i 【参考方案1】:

据我了解,您需要一种“子类化器”,因此您根本不需要参数module(请参阅我的评论)。我使用__new__ 来模仿装饰风格,它使用type 内置函数动态创建一个子类。 __init__ 更痛苦:它需要一个额外的函数,因为 __init__ 必须始终返回 None

class my_dataclass:
    def __new__(cls, tcls):
        sub_cls = type(f'tcls.__name__Jr', (tcls,), )

        def constructor(self, **cfg):
            setattr(self, 'c', super(type(self), self).create_c(**cfg))
            super(type(self), self).__init__()
            
        setattr(sub_cls, '__init__', lambda self, **cfg: constructor(self, **cfg))
        
        return sub_cls


@my_dataclass
class A:
    
    def __init__(self):
        print(self, '__init__')

    def create_c(self, **cfg):
        print(**cfg)
        return 'create_c'
        
print(A)
a = A()
print(a.c) # attribute

输出

<class '__main__.AJr'>

<__main__.AJr object at 0x7f4105dcc2e0> __init__
create_c

【讨论】:

感谢您的回答。我想我需要一些时间来消化你的代码......【参考方案2】:

一个类没有内置的等价于functools.wraps,但你可以自己复制被包装类的dunder属性,包括__doc____name____qualname____module__,作为documented。

def decorate_module(Module):
    class Wrapper(Module):
        def __init__(self, cfg):
            self.c = create_c(**cfg)
            super().__init__()

    for attr in '__doc__', '__name__', '__qualname__', '__module__':
        setattr(Wrapper, attr, getattr(Module, attr))
    return Wrapper

【讨论】:

以上是关于用正确的名字装饰一个类的主要内容,如果未能解决你的问题,请参考以下文章

装饰器

正确使用装饰器

Python 元类与类装饰器

装饰器

类方法的python装饰器

用参数继承类方法装饰器[重复]