0

0

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

DDD

DDD

发布时间:2025-10-03 15:43:46

|

950人浏览过

|

来源于php中文网

原创

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 位置原先指向的对象,而是断开了原有的引用关系,建立了一个新的引用关系。

示例:赋值后的行为

Figma
Figma

Figma 是一款基于云端的 UI 设计工具,可以在线进行产品原型、设计、评审、交付等工作。

下载

现在,我们尝试为 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 # 示例

热门AI工具

更多
DeepSeek
DeepSeek

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

豆包大模型
豆包大模型

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

通义千问
通义千问

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

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

更多
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中文网学习。

1498

2023.10.24

字符串介绍
字符串介绍

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

623

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

592

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

587

2024.04.29

go语言字符串相关教程
go语言字符串相关教程

本专题整合了go语言字符串相关教程,阅读专题下面的文章了解更多详细内容。

170

2025.07.29

c++字符串相关教程
c++字符串相关教程

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

83

2025.08.07

Python 自然语言处理(NLP)基础与实战
Python 自然语言处理(NLP)基础与实战

本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

10

2026.01.27

热门下载

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

精品课程

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

共4课时 | 22.3万人学习

Django 教程
Django 教程

共28课时 | 3.5万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.3万人学习

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

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