【问题标题】:How to refer to nested Python dataclasses? [duplicate]如何引用嵌套的 Python 数据类? [复制]
【发布时间】:2021-12-10 15:07:16
【问题描述】:

类似问答的注意事项:


我正在尝试在另一个类中对多个 Python @dataclass 装饰类进行简单嵌套,并让后面的类引用早期的类。如果我根本不嵌套它们,它们会按预期工作,能够将定义的第一个类包含到第二个类的对象中:

from dataclasses import dataclass, field

@dataclass
class A:
  z:int = field(default=0)

@dataclass
class B:
  a:A = field(default=A(z=1)) ### Object that is class A is included in class B

b = B(a=A(z=3))
print(f'b = B(a=A(z=3)); b.a.z={b.a.z}; b={b}')

但是如果我尝试在另一个类(在这种情况下,不是数据类)中做同样的事情,“B”类就看不到“A”类。在下面的代码中,将a 定义为A 类型失败并出现NameError:“名称A 未定义”。我试过 A 和 C.A,都不行。

请注意,C 类中的其他函数可以同时看到 A 和 B,只是在数据类 B 中看不到数据类 A。

class C:
  @dataclass
  class A:
    z:int = field(default=0)

  @dataclass
  class B:
    a:A = field(default=A(z=1)) ### NameError: name 'A' is not defined

  def __init__(self):
    self.b = C.B(a=C.A(z=3))

  def print_info(self):
    print(f'b = C.B(a=C.A(z=3)); b.a.z={self.b.a.z}; b={b}')

c = C()
c.print_info()

但是,如果我将这些转换为普通的 Python 类,它可以在嵌套情况下工作:

重新检查,事实证明这在普通课程中也被破坏了(根据下面的评论)。

奇怪的是,如果将数据类 A 数据类 B 中嵌套,而 B 仍在类 C 中,它确实可以工作 - B 可以直接访问 A,但 C 类中的其他任何内容都不能直接访问 A .

问题

是否可以定义嵌套数据类,让后面的数据类在同一级别访问早期的数据类?如果有,怎么做?

【问题讨论】:

  • "Flat is better than nested." - The Zen of Python - 我看不出在这里嵌套类的充分理由。另外,请注意,您在这两种情况下所做的是在B 的所有实例之间共享一个A 实例。这可能不是您想要的,因为这些不是 frozen 数据类。
  • 另外,如果您从嵌套数据类示例中删除 @dataclass 装饰器,您会发现它仍然会引发 NameError,因此这与数据类无关......跨度>
  • @AKX 在我“相当复杂”的真实世界代码中,将它们嵌套以获得功能分组感觉是正确的做法,但我确实可以让它们至少在每个模块中保持平坦。关于 NameError - 你是对的!我把文件的布局搞砸了,如果我把它剪成那个例子,它也坏了。
  • 模块非常适合功能分组。我认为标准库中没有很多嵌套类(如果有的话)......
  • 基于我在正常课程上的错误,它是stackoverflow.com/q/42185472/6501141的重复,投票重新关闭它。您的回答确实增加了一些在问答中没有的清晰度。谢谢!

标签: python python-3.x class python-dataclasses


【解决方案1】:

据我所知,这是由于class definitions 的语义——强调我的:

然后使用新创建的本地命名空间和原始全局命名空间在新的执行框架中执行该类的套件。 (通常,套件主要包含函数定义。)当类的套件完成执行时,其执行帧被丢弃,但其本地命名空间被保存。

也就是说,任何类定义都只有模块全局命名空间(在这种情况下还不包含C,因为它的套件尚未完成执行)和一个新的空本地命名空间。

编辑

基于上述,这个可以被破解一起工作,但我真的不会这样做......

from dataclasses import dataclass, field


class C:
    @dataclass
    class A:
        z: int = field(default=0)

    globals()["A"] = A  # "and the original global namespace..."

    @dataclass
    class B:
        a: A = field(default=A(z=1))

    def __init__(self):
        self.b = C.B(a=C.A(z=3))

    def print_info(self):
        print(f"{self.b.a.z=}")


c = C()
c.print_info()

【讨论】:

  • 现在说得通了。嵌套类实际上并不存在,除非它们通过该类的实例存在,除非它们以某种方式被设为静态,以便类可以直接引用它们(这甚至可能不像函数/方法那样) .在这种情况下,我需要拥抱 Python 的禅宗,这意味着扁平化我的类定义,并在我真的需要功能分组时拆分成更多模块......
  • "嵌套类实际上并不存在,除非它们通过该类的实例存在" 不,它们确实存在,只是它们所在的命名空间是本地和匿名的,直到外部类是“完全定义的”。
  • 无论如何,看看我的编辑——通过将内部类注入全局命名空间,这可以工作,但它非常难看。
  • 我认为您仍然可以引用内部类而不必将其设为全局,但您可以通过 C 引用它(因为它是在其中定义的):inner_instance = C.A(z=0)
  • @Richard 我对此表示怀疑,因为在评估内部主体时,名称 C 还不是一个东西。
猜你喜欢
  • 2020-10-30
  • 1970-01-01
  • 1970-01-01
  • 2021-05-22
  • 2014-11-14
  • 2016-04-28
  • 1970-01-01
  • 2022-12-23
  • 1970-01-01
相关资源
最近更新 更多