如何在dataclass中使用带有default_factory的python描述符

bqf10yzr  于 2023-04-28  发布在  Python
关注(0)|答案(1)|浏览(338)

如何使描述符与dataclass和default_factory一起工作?在以下示例中:

from dataclasses import dataclass, field
import numpy as np

class A():
    a = np.arange(2)

class Verbose_attribute(A):
    def __get__(self, obj, type=None) -> object:
        print("accessing the attribute to get the value")
        return self.a[-1]
    def __set__(self, obj, value) -> None:
        print("accessing the attribute to set the value")
        self.a[-1]=value
        
    #a = np.arange(2)

@dataclass
class Foo():
    attribute1: Verbose_attribute = Verbose_attribute()
    #attribute1: Verbose_attribute = field(default_factory=lambda: Verbose_attribute())

my_foo_object = Foo()
print(my_foo_object.attribute1)
my_foo_object.attribute1 = 3
print(my_foo_object.attribute1)

第一次打印工作正常,显示默认值1。如果在Foo()中注解attribute1的第一个版本,而取消注解第二个版本,则第一个打印给出的是object而不是属性值。
使用default_factory对我来说很重要,因为在真实的情况下,我正在为继承自MutableSequence的类创建一个描述符。

iszxjhcz

iszxjhcz1#

你不会直接这么做的。default_factory专门设计为该字段提供一次值。
当使用描述符时,需要有一个指向描述符本身的 class 属性。要将描述符与数据类沿着使用,注解类型(在:之后)必须是描述符将采用/提供的数据类型,而不是描述符本身。
如果您想要默认值,因为您正在编写descritor类,只需在描述符__init____set_name__方法中创建值。
一个额外的注意事项:描述符必须为每个主机类示例保留一组值。如果你试图在描述符本身中使用一个属性(或者更糟,在描述符类中,就像在你的代码中一样),这个值将在你的数据类的所有示例中共享。
无论如何,这应该起作用:

class Verbose_attribute:
    def __init__(self, factory=None):
        if factory is None:
            factory = lambda: np.arange(2)
        self.factory = factory
            
    def __set_name__(self, type, name):
        self.name = name
        self.attrname = f"_{name}_value"
    
    def create_default(self, obj):
        setattr(obj, self.attrname, self.factory())
            
    # if type annotations are not helping, better not 
    # to include them. they are optional, remember.
    def __get__(self, obj, type):
        if not hasattr(obj, self.attrname):
            self.create_default(obj)
        print("accessing the attribute to get the value")
        return getattr(obj, self.attrname)[-1]
        
    def __set__(self, obj, value):
        if not hasattr(obj, self.attrname):
            self.create_default(obj)
        print("accessing the attribute to set the value")
        if isinstance(value, type(self)):
            return
        getattr(obj, self.attrname)[-1]=value

...
from dataclasses import dataclass, field

...

@dataclass
class Foo:
    attribute1: int = field(Verbose_attribute())

更新:在前面的代码中有一个错误,我在__get__方法上做了一个属性-在dataclass.field中有一个奇怪的行为:尽管在Python 3中设计为使用作为default parameter传递的描述符。11.1如果主机数据类是不带参数示例化的,则它尝试将描述符的值设置为自身。这可能是dataclasses中的一个bug-我包含了一个if子句来解决这个问题。默认值由Verbose_attribute类本身中的factory回调提供。

相关问题