简体   繁体   English

python中的自引用类?

[英]Self-referencing classes in python?

In Python, can you have classes with members that are themselves pointers to members of the type of the same class?在 Python 中,您是否可以让类的成员本身指向同一类的类型的成员? For example, in C, you might have the following class for a node in a binary tree:例如,在 C 中,对于二叉树中的节点,您可能有以下类:

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

How would you equivalently create this in python?你将如何在 python 中等效地创建它?

Emulating a C struct in Python (using str instead of int as the data type):在 Python 中模拟 C 结构(使用 str 而不是 int 作为数据类型):

"Declaration": “声明”:

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

Usage:用法:

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

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

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

Python is a dynamic language. Python 是一种动态语言。 Attributes can be bound at (almost) any time with any type.属性可以(几乎)随时与任何类型绑定。 Therefore, the problem you are describing does not exist in Python.因此,您所描述的问题在 Python 中不存在。

While this, as other answers have pointed out, is not a problem due to the dynamic typing, in fact, for Python3, this is a very real issue when it comes to type annotations .虽然正如其他答案所指出的那样,由于动态类型,这不是问题,但实际上,对于 Python3,当涉及到类型注释时,这是一个非常现实的问题。 And this will not work (note a type annotation of the method argument):这将不起作用(注意方法参数的类型注释):

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)

results in:结果:

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

more on the nature of a problem here: https://www.python.org/dev/peps/pep-0484/#the-problem-of-forward-declarations更多关于这里问题的性质: https : //www.python.org/dev/peps/pep-0484/#the-problem-of-forward-declarations

You can use string literals to avoid forward references您可以使用字符串文字来避免前向引用

在此处输入图片说明

Other way is NOT using python3-style type annotation in such cases,在这种情况下,另一种方法是不使用 python3 样式的类型注释,

and this is the only way if you have to keep your code compatible with earlier versions of Python.如果您必须使代码与早期版本的 Python 兼容,这是唯一的方法。

Instead, for the sake of getting autocompletion in my IDE (PyCharm), you can docstrings like this:相反,为了在我的 IDE (PyCharm) 中获得自动完成功能,您可以使用这样的文档字符串:

使用 python 文档字符串而不是类型提示来避免类自引用

Update: alternatively, instead of using docstrings, you can use "type: " annotations in a comment .更新:或者,您可以注释使用“type:”注释,而不是使用文档字符串。 This will also ensure that mypy static type checking will work (mypy doesn't seem to care about docstrings):这也将确保 mypy 静态类型检查将起作用(mypy 似乎并不关心文档字符串):

在此处输入图片说明

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

How would I equivalently create this in python?我将如何在 python 中等效地创建它?

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

Since all Python variables are, in effect, typeless references, you don't have to mention up front that left and right are going to be instances of nodes.由于所有 Python 变量实际上都是无类型引用,因此您不必预先提及 left 和 right 将成为节点的实例。

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

This question deserves a revamp for 2021. Let's get some code then run through a breakdown.这个问题值得在 2021 年改进。让我们获取一些代码,然后分析一下。

from __future__ import annotations
from dataclasses import dataclass

@dataclass
class Node: 
  data: int
  left: Node
  right: Node

As of Python 3.7, we have PEP 563 -- Postponed Evaluation of Annotations , which means one can use type hints to reference the class, inside the class.从 Python 3.7 开始,我们有PEP 563 -- Postponed Evaluation of Annotations ,这意味着可以在类内部使用类型提示来引用类。 It does need to be enabled, which is what our first line of code does.它确实需要启用,这就是我们的第一行代码所做的。

The @dataclass decorator is part of the Data Classes , also added in 3.7, and gives us a convenient way of defining a class, especially one that is primarily members definitions. @dataclass装饰器是Data Classes 的一部分,也在 3.7 中添加,它为我们提供了一种定义类的便捷方式,尤其是主要是成员定义的类。

I have used Node rather than node , since class names are usually capitalised in python, to avoid collision with variables and modules.我使用Node而不是node ,因为类名在 python 中通常大写,以避免与变量和模块发生冲突。 This is a stylistic thing and can be safely ignored most of the time.这是一种风格问题,大多数时候可以安全地忽略。

Finally, from Python 3.10, you can drop the from __future__... import as it will be enabled by default.最后,从 Python 3.10 开始,您可以删除from __future__...导入,因为它将默认启用。 The release date for that is... Oh, it's today!发布日期是……哦,是今天!

I think this might be helpful我认为这可能会有所帮助

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 offers a Self class to reference class itself which I think is most elegent way to self-reference( PEP 673 ). Typing_extensions 提供了一个 Self 类来引用类本身,我认为这是最优雅的自我引用方式( PEP 673 )。


As others have mentioned, you can also use string literals.正如其他人所提到的,您也可以使用字符串文字。 But it comes to problem when you have multiple type hints.但是当你有多个类型提示时就会出现问题。

# python 3.10
var: str | int

And then you write something like然后你写一些类似的东西

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

It will raise an TypeError: unsupported operand type(s) for |: 'str' and 'type'.它将引发一个 TypeError: unsupported operand type(s) for |: 'str' 和 'type'。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM