如何在装饰器中使用“@dataclass”装饰器?

How to use `@dataclass` decorator within a decorator?

我想创建一个 class 装饰器来预先定义一些属性和方法来装饰 class。同时,我想用 @dataclass 装饰器装饰这个装饰 class 。 为了让用户更轻松,我希望用户只需使用我的装饰器,以便 his/her class 被装饰成 @dataclass.

所以这就是我想出的装饰 class,这样装饰后的 class 就有了新的属性 id_。我提交这段代码是想知道decoartor的嵌套是不是'acceptable'? 基本上,我不太确定像函数一样调用装饰器(虽然它是一个函数),所以我想知道是否会有副作用?

from dataclasses import dataclass

# My library: complexitiy is allowed.
def indexer(orig_class):
    orig_class = dataclass(orig_class)
    # Copy of original __init__ to call it without recursion.
    orig_init = orig_class.__init__
    id_ = 10

    def __init__(self,*args, **kws):
        self.id_ = id_
        orig_init(self, *args, **kws) # Call the original __init__

    orig_class.__init__ = __init__ # Set the class' __init__ to the new one

    return orig_class

# User environment: importing @indexer decorator: ease of use is targeted.
# Expected behavior is that @indexer provides same functions as @dataclass.
@indexer
class Truc:
    mu : int

# Test
test = Truc(3)

test.mu
Out[37]: 3

test.id_
Out[38]: 10

顺便说一句,不知道装饰器是否比class继承更好,在添加属性和方法时? 谢谢指教! 最佳,

编辑

谁能读到这里,并且想知道装饰器和继承之间的使用。

在实施中走得更远,我最终坚持使用装饰器,因为在我看来,它提供了一种更灵活的方式来调整如果保留了继承的子项 class。

通过继承,我看不到父 class 如何对子属性有任何控制(例如使它们冻结),同时完全自由地 naming/defining 这些属性。

但这实际上是一个重要的属性我正在寻找数据class。

因此,可以使用以下 @indexer 装饰器查看当前状态。 它让用户可以自由地创建具有 he/she 想要的参数数量的 'data class',并将它们命名为 he/she 想要的。 但它使

成为可能

它满足我的要求(我可以使用从 decorated class 创建的对象)作为字典或排序字典的键。

如果它能帮上忙,就在这里。

PS:如果您有任何建议或更好的做法,我欢迎他们。

from dataclasses import dataclass, asdict

# for optional arguments in decorator: 
def indexer(index_class=None, *, fields_sep:str='.'):
    def _tweak(index_class):
        # Wrap with `@dataclass`.
        index_class = dataclass(index_class, order= True, frozen=True)
        # Copy of original __init__ to call it without recursion.
        index_class_init = index_class.__init__
    
        def __init__(self,*args, **kws):
            object.__setattr__(self, "_fields_sep", fields_sep)
            index_class_init(self, *args, **kws)
    
        index_class.__init__ = __init__
    
        def _to_str(self):
            return self._fields_sep.join(map(str, asdict(self).values()))
    
        index_class._to_str = property(_to_str)
    
        return index_class
    if index_class:
        # Calling decorator without other parameters.
        return _tweak(index_class)
    # Calling decorator with other parameters.
    return _tweak

# Test without parameter.
@indexer
class Test:
    mu : int
    nu : str
test = Test(3, 'oh')
assert test._to_str == '3.oh'

# Test with 'fields_sep' parameter.
@indexer(fields_sep='-')
class Test:
    mu : int
    nu : str
test = Test(3, 'oh')
assert test._to_str == '3-oh'

# Test (un)equality.
@indexer
class Test:
    mu : int
    nu : str
test1 = Test(3, 'oh')
test2 = Test(3, 'oh')
assert test1 == test2
test3 = Test(4, 'oh')
assert test1 != test3

# Test dict.
di = {test1:[1,2,3], test2:[7,8,9]}
assert len(di) == 1
assert di[Test(3, 'oh')] == [7,8,9]

Basically, I am not so sure about calling a decorator like a function (while it is a function), so I wonder if there could be side effects?

装饰器python中是语法糖PEP 318动机给出了以下示例

def foo(cls):
    pass
foo = synchronized(lock)(foo)
foo = classmethod(foo)

等同于

@classmethod
@synchronized(lock)
def foo(cls):
    pass

换句话说,装饰器允许您编写更少的代码行来获得完全相同的结果。