Python列表乘法与引用:深度解析*操作符的行为

DDD
发布: 2025-10-03 15:43:46
原创
911人浏览过

Python列表乘法与引用:深度解析*操作符的行为

本教程深入探讨Python中列表乘法*操作符在处理引用时的行为。它解释了*如何创建对同一对象的浅层引用,以及当对列表元素进行赋值操作时,这些引用如何被重新指向新的对象,尤其是在嵌套列表场景下,这可能导致与预期不符的结果。文章通过代码示例和id()函数展示了引用变化的机制,并提供了避免常见陷阱的正确方法。Python的列表乘法*操作符是一个便捷的工具,用于创建包含重复元素的列表。然而,其在处理对象引用时的行为常令初学者感到困惑,尤其是在涉及嵌套列表时。理解*操作符如何创建引用,以及赋值操作如何影响这些引用,对于编写健壮的Python代码至关重要。

列表乘法 * 的引用机制

当使用 [item] * n 创建列表时,python实际上创建了一个包含 n 个对 item 对象引用的新列表。这意味着所有 n 个元素都指向内存中的同一个对象。对于不可变对象(如整数、字符串、元组),这通常不会引起问题,因为对元素的修改总是创建新对象并重新赋值引用。但对于可变对象(如列表、字典),这种浅层引用机制会导致意外行为。

示例:观察初始引用

假设我们有一个二维数据结构 A,我们希望创建一个与 A 同尺寸的空矩阵 empty_matrix。

# 假设A是一个3x2的矩阵,仅用于确定维度
A = [[0, 0], [0, 0], [0, 0]] 

# 创建一个包含两个None的列表作为行模板
empty_row = [None] * len(A[0]) # 结果:[None, None]

# 使用empty_row创建3个重复的行
empty_matrix = [ empty_row ] * len(A) # 结果:[[None, None], [None, None], [None, None]]

print("--- 初始引用ID ---")
for i in range(len(empty_matrix)):
    # 打印每行列表对象的ID
    print(f"行对象ID: {id(empty_matrix[i])}")
    for j in range(len(empty_matrix[0])):
        # 打印每行中元素对象的ID
        print(f"     元素对象ID: {id(empty_matrix[i][j])}", end = ", ")
    print()
登录后复制

输出分析:

--- 初始引用ID ---
行对象ID: 2856577670848 # 示例ID,实际值会不同
     元素对象ID: 140733388238040,      元素对象ID: 140733388238040, 
行对象ID: 2856577670848
     元素对象ID: 140733388238040,      元素对象ID: 140733388238040, 
行对象ID: 2856577670848
     元素对象ID: 140733388238040,      元素对象ID: 140733388238040, 
登录后复制

从输出可以看出,empty_matrix 中的所有行(empty_matrix[i])都具有相同的ID,这明确表明它们都指向内存中的同一个列表对象 empty_row。同样,empty_row 中的所有元素(None)也指向同一个 None 对象。

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

赋值操作对引用的影响

当对列表的某个元素执行赋值操作(例如 list[index] = new_value)时,Python会改变 list[index] 所存储的引用,使其指向 new_value 对象。这并不会修改 index 位置原先指向的对象,而是断开了原有的引用关系,建立了一个新的引用关系。

示例:赋值后的行为

酷表ChatExcel
酷表ChatExcel

北大团队开发的通过聊天来操作Excel表格的AI工具

酷表ChatExcel48
查看详情 酷表ChatExcel

现在,我们尝试为 empty_matrix 的每个元素赋值:

# 假设A的维度与之前相同,例如3x2
# empty_matrix 仍然是 [[None, None], [None, None], [None, None]],所有行和元素共享引用

for i in range(len(A)): # 遍历行
    for j in range(len(A[0])): # 遍历列
        empty_matrix[i][j] = i*10+j # 赋值操作

print("\n--- 赋值后的矩阵内容 ---")
for r in empty_matrix:
    for c in r:
        print(c, end = ", ")
    print()

print("\n--- 赋值后的引用ID ---")
for i in range(len(empty_matrix)):
    print(f"行对象ID: {id(empty_matrix[i])}")
    for j in range(len(empty_matrix[0])):
        print(f"     元素对象ID: {id(empty_matrix[i][j])}", end = ", ")
    print()
登录后复制

输出分析:

--- 赋值后的矩阵内容 ---
20, 21, 
20, 21, 
20, 21, 

--- 赋值后的引用ID ---
行对象ID: 1782995372160 # 示例ID,与初始行ID相同
     元素对象ID: 1782914902928,      元素对象ID: 1782914902960, 
行对象ID: 1782995372160
     元素对象ID: 1782914902928,      元素对象ID: 1782914902960, 
行对象ID: 1782995372160
     元素对象ID: 1782914902928,      元素对象ID: 1782914902960, 
登录后复制

为何结果是 20, 21, 20, 21, 20, 21 而不是预期的 0, 1, 10, 11, 20, 21?

  1. 行引用不变: empty_matrix 中的所有行仍然指向同一个列表对象(即 empty_row 的原始实例)。从赋值后的 id() 输出中,我们可以看到所有行的ID仍然相同。这意味着对任何 empty_matrix[i] 的修改都会反映在所有行中。
  2. 元素引用改变: 当执行 empty_matrix[i][j] = i*10+j 时,我们改变的是 empty_matrix[i](即那唯一的 empty_row 实例)中索引 j 处的引用。它不再指向 None,而是指向了一个新的整数对象 i*10+j。
  3. 由于所有行都共享同一个内部列表对象,所以对 empty_matrix[0][j] 的赋值实际上修改了所有行共享的那个列表的第 j 个元素。当循环进行到 i=2 时,empty_matrix[2][0] 被赋值为 20,empty_matrix[2][1] 被赋值为 21。由于所有行都引用同一个底层列表,因此所有行都显示为 20, 21。
  4. id() 输出也证实了这一点:虽然行ID保持不变,但行内的元素ID在赋值后已经发生了变化,并且不同列的元素ID也不同了,这说明它们现在指向了不同的整数对象。

如何正确创建独立的嵌套列表

要创建具有独立行的嵌套列表(或矩阵),确保每行都是一个独立的列表对象是关键。最常见且推荐的方法是使用列表推导式,它会为每次迭代生成一个新的列表对象。

A = [[0, 0], [0, 0], [0, 0]] # 3x2 矩阵

# 使用列表推导式创建独立的行
# 每次循环都会生成一个新的 [None] * len(A[0]) 列表对象
correct_matrix = [[None] * len(A[0]) for _ in range(len(A))]

print("\n--- 正确创建的矩阵 (初始引用ID) ---")
for i in range(len(correct_matrix)):
    print(f"行对象ID: {id(correct_matrix[i])}") # 观察:行ID将不同
    for j in range(len(correct_matrix[0])):
        print(f"     元素对象ID: {id(correct_matrix[i][j])}", end = ", ")
    print()

# 赋值操作
for i in range(len(A)):
    for j in range(len(A[0])):
        correct_matrix[i][j] = i*10+j

print("\n--- 正确赋值后的矩阵内容 ---")
for r in correct_matrix:
    for c in r:
        print(c, end = ", ")
    print()

print("\n--- 正确赋值后的引用ID ---")
for i in range(len(correct_matrix)):
    print(f"行对象ID: {id(correct_matrix[i])}") # 观察:行ID依然不同
    for j in range(len(correct_matrix[0])):
        print(f"     元素对象ID: {id(correct_matrix[i][j])}", end = ", ")
    print()
登录后复制

预期输出 (正确行为):

--- 正确创建的矩阵 (初始引用ID) ---
行对象ID: 140733388238040 # 示例ID,与下一行不同
     元素对象ID: 140733388238040,      元素对象ID: 140733388238040, 
行对象ID: 140733388238120 # 示例
登录后复制

以上就是Python列表乘法与引用:深度解析*操作符的行为的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

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