sco*_*mcc 11 python python-dataclasses
我有一个数据类,我想在循环中迭代以吐出每个值。我可以__iter__()很容易地在其中写一个很短的内容,但这是我应该做的吗?我在文档中没有看到任何有关“可迭代”参数或任何内容的内容,但我只是觉得应该有......
这是我所拥有的,再次运行良好。
from dataclasses import dataclass
@dataclass
class MyDataClass:
a: float
b: float
c: float
def __iter__(self):
for value in self.__dict__.values():
yield value
thing = MyDataclass(1,2,3)
for i in thing:
print(i)
# outputs 1,2,3 on separate lines, as expected
Run Code Online (Sandbox Code Playgroud)
这是最好/最直接的方法吗?
Sha*_*ger 15
最简单的方法可能是按照创建浅拷贝的函数中dataclasses.astuple的指导迭代地提取字段,只需省略对 ( 的调用,将其保留为生成器表达式,这是返回tuple的合法迭代器:__iter__
def __iter__(self):
return (getattr(self, field.name) for field in dataclasses.fields(self))
# Or writing it directly as a generator itself instead of returning a genexpr:
def __iter__(self):
for field in dataclasses.fields(self):
yield getattr(self, field.name)
Run Code Online (Sandbox Code Playgroud)
不幸的是,astuple它本身并不适合(因为它递归,解包嵌套的数据类和结构),而asdict(随后.values()对结果进行调用)虽然适合,但涉及急切地构造临时dict并递归复制内容,这是相对重量级的(内存-明智的和CPU方面的);最好避免不必要的O(n)急切工作。
asdict如果您想要/需要避免使用实时视图(如果实例的后续属性在迭代过程中被替换/修改,则asdict不会改变,因为它实际上保证它们是预先深度复制的,而genexpr将是合适的)当您达到新值时反映它们)。使用的实现asdict甚至更简单(如果更慢,由于急切的预深复制):
def __iter__(self):
yield from dataclasses.asdict(self).values()
# or avoiding a generator function:
def __iter__(self):
return iter(dataclasses.asdict(self).values())
Run Code Online (Sandbox Code Playgroud)
还有第三种选择,那就是dataclasses完全放弃。如果您同意让您的类表现得像一个不可变的序列,那么您可以通过将其设为 a typing.NamedTuple(或更旧的、不太灵活的collections.namedtuple)来免费获得可迭代性,例如:
from typing import NamedTuple
class MyNotADataClass(NamedTuple):
a: float
b: float
c: float
thing = MyNotADataClass(1,2,3)
for i in thing:
print(i)
# outputs 1,2,3 on separate lines, as expected
Run Code Online (Sandbox Code Playgroud)
并且它是自动迭代的(您也可以调用len它,索引它或切片它,因为它是tuple具有所有tuple行为的实际子类,它也通过命名属性公开其内容)。