gpt4 book ai didi

python - 一起使用数据类和属性时出现奇怪的问题

转载 作者:行者123 更新时间:2023-12-02 07:15:55 27 4
gpt4 key购买 nike

我在尝试将数据类与属性一起使用时遇到了一个奇怪的问题。

我已经把它缩小到了重现它的最低限度:

import dataclasses

@dataclasses.dataclass
class FileObject:
_uploaded_by: str = dataclasses.field(default=None, init=False)
uploaded_by: str = None

def save(self):
print(self.uploaded_by)

@property
def uploaded_by(self):
return self._uploaded_by

@uploaded_by.setter
def uploaded_by(self, uploaded_by):
print('Setter Called with Value ', uploaded_by)
self._uploaded_by = uploaded_by

p = FileObject()
p.save()

输出:

Setter Called with Value  <property object at 0x7faeb00150b0>
<property object at 0x7faeb00150b0>

我希望得到 None 而不是

我在这里做错了什么或者我偶然发现了一个错误吗?

阅读@juanpa.arrivilillaga的答案后,我认为制作uploaded_by和InitVar可能会解决问题,但它仍然返回一个属性对象。我想是因为他说的是这样的:

the datalcass machinery interprets any assignment to a type-annotated variable in the class body as the default value to the created __init__.

我能找到的使用默认值的唯一选项是从数据类定义中删除 uploadedby 并编写实际的 __init__。这有一个不幸的副作用,即要求您手动为数据类编写 __init__ ,这否定了使用数据类的一些值(value)。这是我所做的:

import dataclasses

@dataclasses.dataclass
class FileObject:
_uploaded_by: str = dataclasses.field(default=None, init=False)
uploaded_by: dataclasses.InitVar=None
other_attrs: str = None

def __init__(self, uploaded_by=None, other_attrs=None):
self._uploaded_by = uploaded_by
self.other_attrs = other_attrs

def save(self):
print("Uploaded by: ", self.uploaded_by)
print("Other Attrs: ", self.other_attrs)

@property
def uploaded_by(self):
if not self._uploaded_by:
print("Doing expensive logic that should not be repeated")
return self._uploaded_by

p = FileObject(other_attrs="More Data")
p.save()

p2 = FileObject(uploaded_by='Already Computed', other_attrs="More Data")
p2.save()

哪些输出:

Doing expensive logic that should not be repeated
Uploaded by: None
Other Attrs: More Data
Uploaded by: Already Computed
Other Attrs: More Data

这样做的负面影响:

  • 你必须编写样板__init__(我的实际用例大约有20 个属性)
  • 您在 __repr__ 中丢失了 uploaded_by,但它仍然存在在_uploaded_by
  • 不调用 asdict、astuple、dataclasses.replace正确处理

所以这确实不能解决问题

我已在 Python Bug Tracker 上提交了一个 Bug: https://bugs.python.org/issue39247

最佳答案

因此,不幸的是,@property 语法始终被解释为对 uploaded_by 的赋值(因为,嗯,它)。 dataclass 机制将其解释为默认值,因此它传递属性对象!它相当于:

In [11]: import dataclasses
...:
...: @dataclasses.dataclass
...: class FileObject:
...: uploaded_by: str
...: _uploaded_by: str = dataclasses.field(repr=False, init=False)
...: def save(self):
...: print(self.uploaded_by)
...:
...: def _get_uploaded_by(self):
...: return self._uploaded_by
...:
...: def _set_uploaded_by(self, uploaded_by):
...: print('Setter Called with Value ', uploaded_by)
...: self._uploaded_by = uploaded_by
...: uploaded_by = property(_get_uploaded_by, _set_uploaded_by)
...: p = FileObject()
...: p.save()
Setter Called with Value <property object at 0x10761e7d0>
<property object at 0x10761e7d0>

本质上是这样的:

In [13]: @dataclasses.dataclass
...: class Foo:
...: bar:int = 1
...: bar = 2
...:

In [14]: Foo()
Out[14]: Foo(bar=2)

我认为没有一个干净的方法来解决这个问题,也许它可以被认为是一个错误,但实际上,不确定解决方案应该是什么,因为本质上,datalcas 机制解释对类型注释的任何分配类体中的变量作为创建的__init__的默认值。您也许可以对 @property 语法进行特殊处理,或者只是 property 对象本身,因此至少 @propertyx = property(set_x, get_x) 是一致的...

需要明确的是,以下作品:

In [22]: import dataclasses
...:
...: @dataclasses.dataclass
...: class FileObject:
...: uploaded_by: str
...: _uploaded_by: str = dataclasses.field(repr=False, init=False)
...: @property
...: def uploaded_by(self):
...: return self._uploaded_by
...: @uploaded_by.setter
...: def uploaded_by(self, uploaded_by):
...: print('Setter Called with Value ', uploaded_by)
...: self._uploaded_by = uploaded_by
...:
...: p = FileObject(None)
...: print(p.uploaded_by)
Setter Called with Value None
None

In [23]: FileObject()
Setter Called with Value <property object at 0x1086debf0>
Out[23]: FileObject(uploaded_by=<property object at 0x1086debf0>)

但请注意,您无法设置有用的默认值!它总是会占用该属性...更糟糕的是,IMO,如果您不想要默认值,它总是会创建一个!

编辑:找到了潜在的解决方法!

这应该是显而易见的,但您可以只在类上设置属性对象。

import dataclasses
import typing
@dataclasses.dataclass
class FileObject:
uploaded_by:typing.Optional[str]=None

def _uploaded_by_getter(self):
return self._uploaded_by

def _uploaded_by_setter(self, uploaded_by):
print('Setter Called with Value ', uploaded_by)
self._uploaded_by = uploaded_by

FileObject.uploaded_by = property(
FileObject._uploaded_by_getter,
FileObject._uploaded_by_setter
)
p = FileObject()
print(p)
print(p.uploaded_by)

关于python - 一起使用数据类和属性时出现奇怪的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59623952/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com