【问题标题】:Python dataclass inheritance with mutable default arguments returns zero-value具有可变默认参数的 Python 数据类继承返回零值
【发布时间】:2020-03-27 23:09:23
【问题描述】:

我正在尝试从包含笛卡尔坐标 x、y 和 z 的 3 个列表中计算类似半径的数量。 以下是重现我面临的问题的最小代码示例; 子类计算半径数量但返回零值。 这是什么原因,如何解决?

脚本:

# -*- coding: utf-8 -*-

from dataclasses import dataclass, field
from typing import List


@dataclass
class LoadHalo:
    x: List = field(default_factory=list)
    y: List = field(default_factory=list)
    z: List = field(default_factory=list)

    def __post_init__(self):
        self.x = [1, 2, 3]
        self.y = [1, 3, 5]
        self.z = [1, 4, 7]


@dataclass
class BinHalo(LoadHalo):
    r: List = field(default_factory=list)

    def __post_init__(self):
        self.r = self.modulus(self.x, self.y, self.z)

    def modulus(self, *args):
        """Modulus of vector of arbitrary size."""
        return sum([i ** 2 for containers in args for i in containers]) ** .5


halo = BinHalo()
print(f"halo.x: {halo.x}")
print(f"halo.r: {halo.r}")

它为 x 和 r 输出以下值:

halo.x: []
halo.r: 0.0

【问题讨论】:

  • self.r = self.modulus(self.x, self.y, self.z)之前添加super().__post_init__()

标签: python python-3.x oop inheritance python-dataclasses


【解决方案1】:

因为你是覆盖__post_init__,所以列表仍然是空的,因为除了空列表默认​​值之外,它们从未被初始化。如果您还想要它的行为,则必须调用要覆盖的超类方法。

你想要的是以下内容:

def __post_init__(self):
    super().__post_init__()
    self.r = self.modulus(self.x, self.y, self.z)

请注意您的类型提示中的几件事:您可能希望 float 用于子类中的 r 字段,以及 modulus 的返回类型:

def modulus(self, *args) -> float:
    ...

此外,您应该使用 float 对象初始化默认列表,以便您可以编写:

x: List[float]

对于您的列表字段,因为大概您想使用浮点数学。

总而言之,我会这样定义:

from dataclasses import dataclass, field
from typing import List


@dataclass
class LoadHalo:
    x: List[float] = field(default_factory=list)
    y: List[float] = field(default_factory=list)
    z: List[float] = field(default_factory=list)

    def __post_init__(self) -> None:
        self.x = [1.0, 2.0, 3.0]
        self.y = [1.0, 3.0, 5.0]
        self.z = [1.0, 4.0, 7.0]


@dataclass
class BinHalo(LoadHalo):
    r: float = 0.0 # or whatever is suitable

    def __post_init__(self) -> None:
        super().__post_init__()
        self.r = self.modulus(self.x, self.y, self.z)

    def modulus(self, *args: List[float]) -> float:
        """Modulus of vector of arbitrary size."""
        return sum([i ** 2 for containers in args for i in containers]) ** .5

【讨论】:

  • 非常清晰的工作示例。正是我想要的。谢谢@juanpa.arrivillaga
  • @GustavRasmussen 不用担心,您应该考虑使用 mypy 对您的代码进行类型检查。
猜你喜欢
  • 2011-01-09
  • 2012-03-06
  • 1970-01-01
  • 2020-10-21
  • 1970-01-01
  • 2023-02-07
  • 2012-05-12
  • 2019-09-12
  • 1970-01-01
相关资源
最近更新 更多