【问题标题】:Self-referencing classes in python?python中的自引用类?
【发布时间】:2011-04-22 03:12:18
【问题描述】:

在 Python 中,您是否可以让类的成员本身是指向同一类类型成员的指针?例如,在 C 语言中,二叉树中的节点可能具有以下类:

struct node {
    int data;
    struct node* left;
    struct node* right;
} 

你将如何在 python 中等效地创建它?

【问题讨论】:

  • 那些是指向类的指针,而不是同一个类。在任何语言中,您都不能拥有具有自己类型成员的类。这是一个无限递归。
  • 你是对的。我很抱歉。然而,这个问题仍然是相关的。
  • 这不是真的,看一个简单的Java链表示例:stackoverflow.com/questions/354875/…
  • @ridecar2,这些字段是指向堆上对象的指针。
  • @ridecar2, @Daniel Pryden:我的措辞不准确。我所说的,可以理解为不正确。我的意思更多是从内存布局的角度来看:内存的实际结构不能包含在其自身中。在Java之类的东西中,引用或指针会处理这个问题,这就是问题中的结构正在做的事情。我并不是说递归数据概念是不可能的(毕竟这就是二叉树节点 :))

标签: python class


【解决方案1】:

Python 是一种动态语言。属性可以(几乎)在任何时间与任何类型绑定。因此,您描述的问题在 Python 中不存在。

【讨论】:

  • 嗯?这没有回答 OP 的问题“”“你将如何在 python 中等效地创建它?”“”
  • @John Machin:显然 OP 认为它确实如此,因为他接受了答案。 :)
  • @John:虽然他写了“你如何在 python 中等效地创建它?”,但他实际上问的是“你如何声明具有当前类类型的成员?”。答案当然是不需要,因为在 Python 中不需要。
  • @musicfreak:“显然”??? OP 的鼠标点击了“勾选”按钮——这既不证明也证明不接受。
  • @John:多年对 IRC 的支持告诉你人们并不总是问他们想要回答的问题。
【解决方案2】:

在 Python 中模拟 C 结构(使用 str 而不是 int 作为数据类型):

“声明”:

class Node(object):
    data = None # str
    left = None # Node object or None
    right = None # Node object or None

用法:

root = Node()
root.data = "foo"

b = Node()
b.data = "bar"
root.left = b

z = Node()
z.data = "zot"
root.right = z

【讨论】:

  • 你在这里声明的是类成员,而不是实例成员,不是吗?
  • 对不起,有人能解释一下上面的评论吗?
  • @amssage:您可能想查看this other question 的答案和/或阅读Python tutorial on classes 的页面。
  • @Russell Borogove:你从来没有意识到什么?
  • 实例继承类变量。我想我在第一次学习 Python OO 时遇到了一些令人困惑的行为,从那时起就完全避免使用类变量。我在 init 中设置了我的所有实例默认值。
【解决方案3】:

http://code.activestate.com/recipes/286239-binary-ordered-tree/ 是使用该结构创建的示例二叉树。

【讨论】:

    【解决方案4】:

    我如何在 python 中等效地创建它?

    class node( object ):
        def __init__( self, data, left, right ):
            self.data = data
            self.left = left
            self.right = right
    

    由于所有 Python 变量实际上都是无类型引用,因此您不必预先提及 left 和 right 将是节点的实例。

    【讨论】:

      【解决方案5】:

      您不能在 Python 中声明类型 - 因此,在 Python 中声明类型没有问题。

      【讨论】:

        【解决方案6】:

        虽然正如其他答案所指出的那样,由于动态类型,这不是问题,但事实上,对于 Python3,这对于 类型注释 来说是一个非常现实的问题。这不起作用(注意方法参数的类型注释):

        class A:
            def do_something_with_other_instance_of_a(self, other: A):
                print(type(other).__name__)
        
        instance = A()
        other_instance = A()
        
        instance.do_something_with_other_instance_of_a(other_instance)
        

        结果:

           def do_something_with_other_instance_of_a(self, other: A):
           NameError: name 'A' is not defined
        

        更多关于问题性质的信息: https://www.python.org/dev/peps/pep-0484/#the-problem-of-forward-declarations

        您可以使用字符串文字来避免前向引用

        在这种情况下,其他方法是不使用 python3 样式的类型注释,

        如果您必须保持代码与早期版本的 Python 兼容,这是唯一的方法。

        相反,为了在我的 IDE (PyCharm) 中获得自动完成功能,您可以使用这样的文档字符串:

        更新: 或者,您可以在在评论中使用“type:”注释,而不是使用文档字符串。这也将确保 mypy 静态类型检查能够正常工作(mypy 似乎并不关心文档字符串):

        【讨论】:

        • 谢谢!那是我的问题,然后谷歌把我带到了这里。
        • 另一种选择:之前定义一个空的class same_name: pass
        • str 注释的想法是天才!!!!!!!!!尤其是对于一些只使用一次的类型。
        【解决方案7】:

        这个问题值得在 2021 年进行修改。让我们获取一些代码,然后进行细分。

        from __future__ import annotations
        from dataclasses import dataclass
        
        @dataclass
        class Node: 
          data: int
          left: Node
          right: Node
        

        从 Python 3.7 开始,我们有 PEP 563 -- Postponed Evaluation of Annotations,这意味着可以在类内部使用类型提示来引用该类。它确实需要启用,这是我们的第一行代码所做的。

        @dataclass 装饰器是 Data Classes 的一部分,也是在 3.7 中添加的,它为我们提供了一种方便的定义方式 一个类,尤其是一个主要是成员定义的类。

        我使用Node 而不是node,因为在python 中类名通常大写,以避免与变量和模块发生冲突。这是一个风格问题,大多数时候可以放心地忽略。

        最后,从 Python 3.10 开始,您可以删除 from __future__... 导入,因为它将默认启用。发布日期是……哦,今天!

        【讨论】:

          【解决方案8】:

          我认为这可能会有所帮助

          from typing_extensions import Self
          
          
          class Node:
              """Binary tree node."""
          
              def __init__(self, left: Self, right: Self):
                  self.left = left
                  self.right = right
          

          typing_extensions 提供了一个 Self 类来引用类本身,我认为这是最优雅的自我引用方式(PEP 673)。


          正如其他人所提到的,您也可以使用字符串文字。但是当你有多个类型提示时就会出现问题。

          # python 3.10
          var: str | int
          

          然后你写类似的东西

          class Node:
              def __init__(self, var: 'Node' | SomeClass):
                  self.var = var
          

          它会引发 TypeError: unsupported operand type(s) for |: 'str' and 'type'。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-01-26
            • 2016-07-11
            • 2011-09-07
            • 1970-01-01
            • 2019-05-10
            • 2021-10-12
            相关资源
            最近更新 更多