0

0

深入理解Python中的对象引用与链表构建

心靈之曲

心靈之曲

发布时间:2025-11-28 14:04:59

|

271人浏览过

|

来源于php中文网

原创

深入理解python中的对象引用与链表构建

Python中的变量并非直接存储数据,而是作为指向内存中实际数据对象的“引用”。这种引用机制在构建和操作复杂数据结构(如链表、树等)时尤为关键。许多初学者可能会对变量赋值、属性访问以及对象之间的关联产生困惑,尤其是在链表这种通过`next`属性连接节点的场景下。本文旨在通过一个具体的链表示例,深入剖析Python中对象引用和属性赋值的工作原理,澄清“自动填充”属性的误解。

Python对象引用基础

在Python中,当你创建一个对象并将其赋值给一个变量时,该变量实际上是持有该对象的内存地址(引用)。多个变量可以引用同一个对象,此时它们都指向内存中的同一个实体。我们可以使用内置函数id()来获取一个对象的唯一标识符,从而判断两个变量是否引用了同一个对象。

例如:

a = [1, 2, 3]
b = a
c = [1, 2, 3]

print(f"id(a): {id(a)}")
print(f"id(b): {id(b)}")
print(f"id(c): {id(c)}")
print(f"a is b: {a is b}") # True, a和b引用同一个对象
print(f"a is c: {a is c}") # False, a和c引用不同的对象,尽管内容相同

链表节点与属性赋值

我们以一个简单的单向链表节点为例:

立即学习Python免费学习笔记(深入)”;

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

这个ListNode类有两个属性:val用于存储节点的值,next用于存储指向下一个节点的引用。默认情况下,next被初始化为None,表示没有下一个节点。

逐步解析链表构建过程

我们将通过分析提供的代码片段,一步步理解链表的构建和引用变化。

阶段一:初始化与第一个链接

x = ListNode(3)       # 创建第一个节点,x 引用它
headNode = x          # headNode 也引用第一个节点
y = ListNode(4)       # 创建第二个节点,y 引用它

x.next = y            # 将 x 引用的节点的 next 属性设置为 y 引用的节点
print(f'ID of y: {id(y)}')
print(f'Current x.next:\n\t.val: {x.next.val}\t.next:{x.next.next},\ncurrent headNode.next.next: {headNode.next.next}\n')

解析:

MakeSong
MakeSong

AI音乐生成,生成高质量音乐,仅需30秒的时间

下载
  1. x = ListNode(3): 在内存中创建了一个 ListNode 对象(我们称之为“节点A”,val=3, next=None),变量 x 现在引用着节点A。
  2. headNode = x: 变量 headNode 也开始引用节点A。此时,x 和 headNode 指向同一个对象。
  3. y = ListNode(4): 在内存中创建了另一个 ListNode 对象(我们称之为“节点B”,val=4, next=None),变量 y 现在引用着节点B。注意,节点A和节点B是两个不同的对象。
  4. x.next = y: 这一步是关键。它不是改变 x 变量的引用,而是修改 x 所引用对象(节点A)的 next 属性。现在,节点A的 next 属性引用着节点B。

输出分析:

  • x.next: x 引用节点A,节点A的next属性引用节点B。所以x.next就是节点B。其val为4,next为None。
  • x.next.next: x.next是节点B,节点B的next属性是None。所以x.next.next是None。
  • headNode.next.next: headNode引用节点A,节点A的next属性引用节点B,节点B的next属性是None。所以headNode.next.next是None。

这个阶段,我们手动设置了节点A的next属性指向节点B。

阶段二:延伸链表与变量重定向

x = y                 # x 现在引用节点B (之前 y 引用的对象)
y = ListNode(4)       # 创建第三个节点,y 引用它
x.next = y            # 将 x 引用的节点 (节点B) 的 next 属性设置为 y 引用的节点 (节点C)
print(f'ID of y: {id(y)}')
print(f'Current x.next:\n\t.val:{x.next.val}\t.next:{x.next.next},\ncurrent headNode.next.next: {headNode.next.next.val}\n')

x = y                 # x 现在引用节点C

print(f'Cached list: [{headNode.val}] -> [{headNode.next.val}] -> [{headNode.next.next.val}]')

解析:

  1. x = y: 这一步改变了变量 x 的引用目标。之前 x 引用节点A,y 引用节点B。现在 x 不再引用节点A,而是和 y 一样,引用节点B。
  2. y = ListNode(4): 在内存中创建了第三个 ListNode 对象(我们称之为“节点C”,val=4, next=None)。变量 y 现在引用着节点C。请注意,此时 y 不再引用节点B,但 x 仍然引用节点B。
  3. x.next = y: 这一步修改了 x 所引用对象(节点B)的 next 属性。现在,节点B的 next 属性引用着节点C。

输出分析:

  • x.next: x 引用节点B,节点B的next属性引用节点C。所以x.next就是节点C。其val为4,next为None。
  • x.next.next: x.next是节点C,节点C的next属性是None。所以x.next.next是None。
  • headNode.next.next: 这是最容易产生“自动填充”错觉的地方。
    • headNode 始终引用最初的节点A。
    • headNode.next 引用节点A的next属性,也就是节点B。
    • headNode.next.next 引用节点B的next属性。在 x.next = y 这一步中,我们修改了节点B的next属性使其指向节点C。因此,headNode.next.next现在就是节点C,其val为4。

最终的Cached list输出显示了整个链条: headNode (节点A) -> headNode.next (节点B) -> headNode.next.next (节点C)。

核心概念总结:无自动填充

从上述分析可以看出,Python中并没有所谓的“自动填充”属性的行为。每次属性值的变化,都是通过显式的赋值操作完成的:

  • x = ListNode(3) 这样的语句是创建新对象并将变量 x 绑定到该对象。
  • x.next = y 这样的语句是修改 x 当前所引用对象的 next 属性,使其指向 y 所引用对象

headNode.next.next之所以会“更新”,是因为 headNode 始终引用着链表的头部节点,而我们通过其他变量(如 x)修改了链表中后续节点的属性。这些修改会沿着引用链条反映出来,因为所有变量最终都指向内存中的特定对象,而这些对象的属性是共享的。

注意事项与最佳实践

  1. 区分变量赋值与属性赋值:
    • variable = value:改变变量variable所引用的对象。
    • object.attribute = value:改变object的attribute属性的值(使其引用value)。
  2. 追踪引用链条: 在处理链表、树等复杂数据结构时,务必在脑海中或通过图示清晰地追踪每个变量当前引用的是哪个对象,以及每个对象的属性指向何处。
  3. id() 的作用: 使用 id() 函数可以帮助你确认不同的变量是否引用了同一个对象,这对于调试引用问题非常有用。
  4. 不可变与可变对象: 虽然本文主要讨论引用,但理解Python中不可变对象(如数字、字符串、元组)和可变对象(如列表、字典、自定义类实例)的行为差异,对于深入理解引用机制也很有帮助。对不可变对象的“修改”实际上是创建了一个新对象并改变引用,而对可变对象的修改则是在原地进行。

总结

Python中的对象引用机制是其强大且灵活的特性之一。通过理解变量作为对象的引用、属性作为对象内部状态的组成部分,以及所有属性赋值都是显式操作这一核心原则,我们可以避免对“指针”和“自动填充”的误解。在构建和操作链表等数据结构时,清晰地追踪对象的引用关系是理解程序行为的关键。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
mysql标识符无效错误怎么解决
mysql标识符无效错误怎么解决

mysql标识符无效错误的解决办法:1、检查标识符是否被其他表或数据库使用;2、检查标识符是否包含特殊字符;3、使用引号包裹标识符;4、使用反引号包裹标识符;5、检查MySQL的配置文件等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

183

2023.12.04

Python标识符有哪些
Python标识符有哪些

Python标识符有变量标识符、函数标识符、类标识符、模块标识符、下划线开头的标识符、双下划线开头、双下划线结尾的标识符、整型标识符、浮点型标识符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

286

2024.02.23

java标识符合集
java标识符合集

本专题整合了java标识符相关内容,想了解更多详细内容,请阅读下面的文章。

258

2025.06.11

c++标识符介绍
c++标识符介绍

本专题整合了c++标识符相关内容,阅读专题下面的文章了解更多详细内容。

124

2025.08.07

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

298

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

212

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1500

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

623

2023.11.24

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

158

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.3万人学习

Django 教程
Django 教程

共28课时 | 3.6万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号