Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 数据类和属性装饰器_Python_Python 3.7_Python Dataclasses - Fatal编程技术网

Python 数据类和属性装饰器

Python 数据类和属性装饰器,python,python-3.7,python-dataclasses,Python,Python 3.7,Python Dataclasses,我一直在阅读Python3.7的dataclass作为namedtuples的替代品(我通常在必须将数据分组到结构中时使用)。我想知道dataclass是否与属性装饰器兼容,以便为dataclass的数据元素定义getter和setter函数。如果是这样的话,这是在什么地方描述的吗?或者是否有可用的示例 它确实有效: from dataclasses import dataclass @dataclass class Test: _name: str="schbell" @p

我一直在阅读Python3.7的dataclass作为namedtuples的替代品(我通常在必须将数据分组到结构中时使用)。我想知道dataclass是否与属性装饰器兼容,以便为dataclass的数据元素定义getter和setter函数。如果是这样的话,这是在什么地方描述的吗?或者是否有可用的示例

它确实有效:

from dataclasses import dataclass

@dataclass
class Test:
    _name: str="schbell"

    @property
    def name(self) -> str:
        return self._name

    @name.setter
    def name(self, v: str) -> None:
        self._name = v

t = Test()
print(t.name) # schbell
t.name = "flirp"
print(t.name) # flirp
print(t) # Test(_name='flirp')
其实,为何不可以呢?最后,您得到的只是一个很好的旧类,派生自类型:

print(type(t)) # <class '__main__.Test'>
print(type(Test)) # <class 'type'>
print(type(t))#
打印(类型(测试))#
也许这就是为什么没有特别提到财产的原因。但是,本文提到了众所周知的Python类特性的一般可用性:

因为数据类使用普通的类定义语法,所以您是自由的 要使用继承、元类、docstring和用户定义的方法, 类工厂和其他Python类特性


目前,我找到的最佳方法是在单独的子类中按属性覆盖dataclass字段

从数据类导入数据类,字段
@数据类
A类:
x:int=0
A类(_A):
@财产
def x(自)->int:
返回自我
@x、 塞特
def x(自身,值:int):
self._x=值
该类的行为类似于常规数据类。并将正确定义
\uuuu repr\uuuu
\uuuu init\uuuu
字段(
A(x=4)
),而不是
A(\ux=4)
。缺点是属性不能是只读的

,尝试用同名的
属性
覆盖wheels数据类属性。 但是,
@属性
会覆盖默认的
字段
,从而导致意外行为

从数据类导入数据类,字段
@数据类
A类:
x:int
#与:`x=property(x)#覆盖任何字段()信息相同`
@财产
def x(自)->int:
返回自我
@x、 塞特
def x(自身,值:int):
self._x=值
A()#`A(x=)`Oups
打印(A.。uuuu数据类_uu字段_uuu)#{'x':字段(name='x',type=,default=,init=True,repr=True}
解决这个问题的一种方法是,在调用dataclass元类之后,覆盖类定义之外的字段,同时避免继承

@dataclass
class A:
  x: int

def x_getter(self):
  return self._x

def x_setter(self, value):
  self._x = value

A.x = property(x_getter)
A.x = A.x.setter(x_setter)

print(A(x=1))
print(A())  # missing 1 required positional argument: 'x'

通过创建一些自定义元类并设置一些
字段(metadata={'setter':\ux\usetter,'getter':\ux\ugetter}),可能会自动覆盖此内容。

一些包装可能很好:

#做你他妈想公开许可的事
#第2版,2004年12月
# 
#版权所有(C)2020徐思远
# 
#每个人都被允许逐字复制和分发或修改
#此许可证文档的副本,并允许在
#随着名字的改变。
# 
#你他妈的想做什么就做什么
#复制、分发和修改的条款和条件
# 
#你他妈的想干什么就干什么。
从数据类导入数据类,字段
缺少=对象()
__all_uuu=['property_字段','property_数据类']
类属性\u字段:
定义初始化(self,fget=None,fset=None,fdel=None,doc=None,**kwargs):
self.field=字段(**kwargs)
self.property=属性(fget、fset、fdel、doc)
def吸气剂(自身,fget):
self.property=self.property.getter(fget)
回归自我
def设定器(自身、fset):
self.property=self.property.setter(fset)
回归自我
def删除器(自身、fdel):
self.property=self.property.deleter(fdel)
回归自我
def属性_数据类(cls=缺失,/,**kwargs):
如果缺少cls:
返回lambda cls:property_数据类(cls,**kwargs)
记住={}
对于k in dir(cls):
如果isinstance(getattr(cls,k),属性字段):
记住[k]=getattr(cls,k).property
setattr(cls,k,getattr(cls,k).field)
结果=数据类(**kwargs)(cls)
对于k,p在.items()中:
设置属性(结果,k,p)
返回结果
您可以这样使用它:

@property\u数据类
B类:
x:int=property\u字段(默认工厂=int)
@x、 吸气剂
def x(自我):
返回自我
@x、 塞特
def x(自身,值):
self._x=值

根据上面的想法,我创建了一个类装饰函数
resolve\u abc\u prop
,它创建了一个新类,其中包含建议的getter和setter函数 作者@shmee

def resolve_abc_prop(cls):
def gen_抽象_属性():
“”“在超类中搜索抽象属性”“”
对于cls中的obj类。mro:
对于键,类\u obj.\u dict\u.items()中的值:
如果是实例(值、属性)和值。\u isabstractmethod\uuuuu:
屈服键、屈服值
abstract\u prop=dict(gen\u abstract\u properties())
def gen_get_set_属性():
“”对于每个匹配的数据和抽象属性对,
创建getter和setter方法“”
对于cls中的obj类。mro:
如果类对象中的“\uuuuu数据类\uu字段”\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
对于键,类对象中的值。字段():
如果输入抽象属性:
def get_func(自身,键=键):
返回getattr(self,f''.{key}')
def set_func(self,val,key=key):
返回setattr(self,f'{key}',val)
屈服键,属性(get_func,set_func)
get\u set\u properties=dict(gen\u get\u set\u properties())
new_cls=类型(
cls.\uuuuuu名称\uuuuuuuuu,
cls.\uuuuMRO\uuuuuuuuuuu,
{**cls.\uuuuu dict\uuuuuu,**get\u set\u properties},
)
返回新的\u cls
这里我们定义一个数据类
AData
和一个mixin
AOpMixin
实现操作 关于数据

从数据类导入数据类,字段,替换
来自abc i
from dataclasses import dataclass, field

@dataclass
class Test:
    name: str
    _name: str = field(init=False, repr=False)

    @property
    def name(self) -> str:
        return self._name

    @name.setter
    def name(self, name: str) -> None:
        self._name = name
my_test = Test(name='foo')
my_test.name = 'bar'
my_test.name('foobar')
print(my_test.name)
from dataclasses import dataclass, field


@dataclass
class Test:
    name: str
    _name: str = field(init=False, repr=False, default='baz')

    @property
    def name(self) -> str:
        return self._name

    @name.setter
    def name(self, value: str) -> None:
        if type(value) is property:
            # initial value not specified, use default
            value = Test._name
        self._name = value


def main():
    obj = Test(name='foo')
    print(obj)                  # displays: Test(name='foo')

    obj = Test()
    obj.name = 'bar'
    print(obj)                  # displays: Test(name='bar')

    obj = Test()
    print(obj)                  # displays: Test(name='baz')


if __name__ == '__main__':
    main()
from dataclasses import dataclass


@dataclass
class Test:
    name: str = 'foo'

    @property
    def _name(self):
        return self._my_str_rev[::-1]

    @_name.setter
    def _name(self, value):
        self._my_str_rev = value[::-1]


# --- has to be called at module level ---
Test.name = Test._name


def main():

    obj = Test()
    print(obj)                      # displays: Test(name='foo')

    obj = Test()
    obj.name = 'baz'
    print(obj)                      # displays: Test(name='baz')

    obj = Test(name='bar')
    print(obj)                      # displays: Test(name='bar')


if __name__ == '__main__':
    main()
Test(name='schbell')
schbell
{'name': 'not-schbell', '_name': 'not-schbell'}
Test(name='llebhcs')
llebhcs
{'name': 'llebhcs', '_name': 'llebhcs'}
from dataclasses import dataclass, InitVar, field, asdict

@dataclass
class D:
    a: float = 10.                # Normal attribut with a default value
    b: InitVar[float] = 20.       # init-only attribute with a default value 
    c: float = field(init=False)  # an attribute that will be defined in __post_init__
    
    def __post_init__(self, b):
        if not isinstance(getattr(D, "a", False), property):
            print('setting `a` to property')
            self._a = self.a
            D.a = property(D._get_a, D._set_a)
        
        print('setting `c`')
        self.c = self.a + b
        self.d = 50.
    
    def _get_a(self):
        print('in the getter')
        return self._a
    
    def _set_a(self, val):
        print('in the setter')
        self._a = val


if __name__ == "__main__":
    d1 = D()
    print(asdict(d1))
    print('\n')
    d2 = D()
    print(asdict(d2))

setting `a` to property
setting `c`
in the getter
in the getter
{'a': 10.0, 'c': 30.0}


in the setter
setting `c`
in the getter
in the getter
{'a': 10.0, 'c': 30.0}
from dataclasses import dataclass


@dataclass
class Person:
    name: str = property

    @name
    def name(self) -> str:
        return self._name

    @name.setter
    def name(self, value) -> None:
        self._name = value

    def __post_init__(self) -> None:
        if isinstance(self.name, property):
            self.name = 'Default'
print(Person().name)  # Prints: 'Default'
print(Person('Joel').name)  # Prints: 'Joel'
print(repr(Person('Jane')))  # Prints: Person(name='Jane')
@dataclass
def SomeData:
    uid: str
    _uid: ClassVar[str]

    @property
    def uid(self) -> str:
        return self._uid

    @uid.setter
    def uid(self, uid: str) -> None:
        self._uid = uid
@dataclass
class Test:
    x: int = 1

    def __setattr__(self, prop, val):
        if prop == "x":
            self._check_x(val)
        super().__setattr__(prop, val)

    @staticmethod
    def _check_x(x):
        if x <= 0:
            raise ValueError("x must be greater than or equal to zero")