Python中的变量、赋值及函数的参数传递概要

article/2025/7/22 7:02:27

Python中的变量、赋值及函数的参数传递概要

python中的变量、赋值

python中的变量不是盒子。

python中的变量无法用“变量是盒子”做解释。图说明了在 Python 中为什么不能使用盒子比喻,而便利贴则指出了变量的正确工作方式。

如果把变量想象为盒子,那么无法解释 Python 中的赋值;应该把变量视作便利贴,这样示例中的行为就好解释了

注意:

对引用式变量来说,说把变量分配给对象更合理,反过来说就有问题。毕竟,对象在赋值之前就创建了。

上面这一段是《流畅的Python》说法。

在 Python 中,一切皆为对象。对象分为不可变对象和可变对象。每个对象都有各自的 id、type 和 value。

Id:当一个对象被创建后,在对象生命周期内它的 id 就不会在改变,在 CPython 实现中,id 通常等于对象的内存地址,可以使用 id() 去查看对象在内存中地址。不可对象(如元组)可能包含可变子对象,子对象的修改不会影响容器的 id。

Type:对象类型(如 int, str, list),决定了支持的操作,通过 type() 获取。

Value:对象存储的具体数据。

可变性的本质

    可变性取决于对象是否允许 原地修改(即不改变 id 的情况下修改 value)。

    不可变对象看似“修改”时,实际上是创建新对象并重新赋值给变量。(不可变对象的“不可变”是指 value 不可原地修改,而非完全不可变。任何对不可变对象的“修改”都会生成新对象。)

# 不可变对象示例(整数)
a = 10
print(id(a))  # 输出初始 id
a += 5
print(id(a))  # id 改变,因为创建了新对象# 可变对象示例(列表)
b = [1, 2]
print(id(b))  # 输出初始 id
b.append(3)
print(id(b))  # id 不变,原地修改

学过C/C++可知,python与C/C++不一样,它的变量使用有自己的特点。下面,就进行必要的展开介绍。

在Python中,变量赋值的机制涉及到对象的引用和对象的可变性。变量名只是对象的引用,而不是直接存储数据。因此,变量a和b赋值后的行为差异,取决于两个因素:

在Python中,数据对象被明确划分为两大阵营:可变(Mutable)与不可变(Immutable)。

不可变数据类型

    数字类型(int/float/complex)

    字符串(str)

    元组(tuple)

    不可变集合/冻结集合(frozenset)

    布尔值(bool)

不可变数据类型的核心特性

1.内存机制:当尝试修改不可变对象时,Python不会改变原对象,而是创建一个新对象。例如:

a = 10
b = a
a += 5  # a指向新对象15,不可变对象的重新赋值,无法原地修改,必须生成新对象
print(id(a), id(b))  # 输出不同内存地址
print(id(a) == id(b))  # 输出 False

2. 内存效率优化

对象复用:Python对部分不可变对象(如小整数范围-5到256、部分短字符串)会进行缓存优化,相同值的对象可能复用同一内存地址。

垃圾回收:不可变对象更易被识别为垃圾,提升回收效率。

3. 哈希性能提升

快速查找:不可变对象的哈希值在创建时确定且不可变,适合作为字典键。字典查找时间复杂度O(1)。

安全保障:哈希值稳定性防止字典键冲突导致的逻辑错误。

4. 线程安全保证

不可变对象天然线程安全,因为无法被修改;可变对象在多线程环境中需使用锁(如 threading.Lock)来保证数据一致性。

不可变 vs 可变

特性

不可变类型

可变类型

内存占用

低(注:不可变类型的低内存占用仅适用于可复用的对象如小整数,大对象可能仍会独立存储)

高(独立副本)

修改成本

高(需创建新对象)

低(原地修改)

线程安全

否(需加锁)

哈希支持

适用场景

字典键、线程共享数据

频繁修改的数据集合

☆不可变对象的值一旦创建,就不能修改其内容

1) 修改变量的新赋值

a = 5         # a指向整数对象5
b = a         # b也指向同一个整数对象5
a = 6         # 此时a指向新的整数对象6,但b仍指向5
print(b)      # 输出5

解释原因:

赋值操作 b = a 时,仅复制引用,使得 a 和 b 都指向同一个对象(初始是5)。

当 a = 6 时,a 的引用被更新为指向新对象6,而 b 仍然指向原对象

2)不可变对象无法被修改内容,只能重新创建新对象,尝试直接修改对象内容(无效)

a = "hello"   # a指向字符串"hello"
b = a         # b也指向相同的字符串对象
# 尝试修改字符串内容(但不可变对象不允许此操作)
a[0] = "H"    # 报错:'str' object does not support item assignment

☆可变对象的值可以在创建后被修改

1) 直接修改对象内容

a = [1, 2]    # a指向列表[1, 2]
b = a         # b指向同一个列表对象
a.append(3)   # 修改列表内容,原列表变为[1, 2, 3]
print(b)      # 输出[1, 2, 3]

解释原因:

a 和 b 指向同一个列表对象。

append() 方法直接修改了该对象的内部内容,因此两个变量看到的都是同一个被修改后的对象。

可变对象(如列表)在修改时可能触发动态扩容,导致内存地址变化(如 a = a + [3] 会创建新列表,内存地址改变,而 a.append(3) 是原地修改,内存地址不变)

2) 修改变量的引用(不影响对方)

a = [1, 2]
b = a
a = [3, 4]    # a的引用被更新为新列表[3,4],但b仍指向原列表[1,2]
print(b)      # 输出[1, 2]

解释原因:

a = [3,4] 是一个新的赋值操作,直接改变了a的引用,使其指向新对象,而b未被修改,依然指向原列表。

python函数的参数传递

python函数的参数传递,也有自己的特点。当传过来的是可变类型(list、dict)时,我们在函数内部修改就会影响函数外部的变量。而传入的是不可变类型时在函数内部修改改变量并不会影响函数外部的变量,因为修改的时候会先复制一份再修改。

Python的参数传递,有人建议表述为:Python的参数传递是按对象引用传递(pass by object reference),即函数接收的是对象的引用副本,而非对象本身的副本。

官方术语,参数传递使用按值调用(call by value)的方式(其中的值始终是对象的引用,而不是对象的值)。实际上,Python函数参数传递的始终对象的引用,而不是对象的值。这方面的内容,因一些人和资料介绍的比较混乱,特地附注。

【附注(有关官方文档节选——同时给出python官方的英文和中文两种说法节选和链接——供参考):

The actual parameters (arguments) to a function call are introduced in the local symbol table of the called function when it is called; thus, arguments are passed using call by value (where the value is always an object reference, not the value of the object). [1] When a function calls another function, or calls itself recursively, a new local symbol table is created for that call.

https://docs.python.org/3/tutorial/controlflow.html#defining-functions

在调用函数时会将实际参数(实参)引入到被调用函数的局部符号表中;因此,实参是使用 按值调用 来传递的(其中的 值 始终是对象的 引用 而不是对象的值)。 [1] 当一个函数调用另外一个函数时,会为该调用创建一个新的局部符号表。

https://docs.python.org/zh-cn/3/tutorial/controlflow.html#defining-functions

Remember that arguments are passed by assignment in Python. Since assignment just creates references to objects, there’s no alias between an argument name in the caller and callee, and so no call-by-reference per se.

https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference

请记住,Python 中的实参是通过赋值传递的。由于赋值只是创建了对象的引用,所以调用方和被调用方的参数名都不存在别名,本质上也就不存在按引用调用的方式。

https://docs.python.org/zh-cn/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference 】

这种机制,也有人称为按对象引用调用(call by object reference),但机制本质不变。

对于不可变对象(如整数、字符串、元组),因为其不可变性,函数内对参数的任何修改不会影响到外部变量。对于可变对象(如列表、字典、集合),函数内对参数的修改会影响到外部变量。

如果传入的参数是不可变类型(如数字、字符串、元组),那么在函数体内修改参数的值,并不会影响到原来的变量。因为不可变类型的变量实际上是值的引用,当试图改变变量的值时,相当于是在创建新的对象。例如:

def change_number(num):num = 100x = 10
change_number(x)
print(x)  # 输出:10

在上面的例子中,尽管在函数内部num的值被改变了,但是原变量x的值并没有改变。参见下图:

如果传入的参数是可变类型(如列表、字典),那么在函数体内修改参数的值,会影响到原来的变量。因为可变类型的变量存储的是一个地址,当试图改变变量的值时,实际上是在改变这个地址所指向的内容。例如:

def change_list(lst):lst.append(100)x = [1, 2, 3]
change_list(x)
print(x)  # 输出:[1, 2, 3, 100]

在上面的例子中,函数内部对参数lst的修改影响到了原变量x的值。参见下图:

浅拷贝与深拷贝在函数参数传递中的作用

函数参数传递使用可变对象时,可能涉及浅拷贝问题(如传入列表时,函数内修改会影响原对象),并建议使用 copy.deepcopy() 避免副作用。

  • 浅拷贝copy.copy()或切片操作(如lst[:]),仅复制顶层对象,嵌套对象仍共享引用。
  • 深拷贝copy.deepcopy(),递归复制所有嵌套对象,生成完全独立的副本。
  • 选择依据:根据数据结构复杂度和是否需要隔离修改来决定。在涉及嵌套可变对象时,优先使用深拷贝。

通过合理使用copy.deepcopy(),可以确保函数对参数的修改不会意外影响外部数据。

假设有一个函数接收一个列表参数,并在内部修改该列表。由于Python的参数传递是对象引用传递,直接修改会影响原对象:

def modify_list(lst):lst.append(4)  # 原地修改列表print("函数内列表:", lst)original_list = [1, 2, 3]
modify_list(original_list)
print("函数外列表:", original_list)

输出:

函数内列表: [1, 2, 3, 4]
函数外列表: [1, 2, 3, 4]  # 原列表被修改

如果希望避免修改原列表,可以使用浅拷贝(copy.copy()或切片操作),但对于嵌套的可变对象,浅拷贝可能不够:

import copydef modify_list_shallow(lst):lst_copy = copy.copy(lst)  # 浅拷贝lst_copy.append(4)print("函数内列表:", lst_copy)original_list = [1, 2, 3]
modify_list_shallow(original_list)
print("函数外列表:", original_list)

输出:

函数内列表: [1, 2, 3, 4]
函数外列表: [1, 2, 3]  # 原列表未被修改

但当列表包含其他可变对象时,浅拷贝仍可能影响原对象:

def modify_nested_list_shallow(lst):lst_copy = copy.copy(lst)  # 浅拷贝lst_copy[0].append(100)    # 修改嵌套列表print("函数内列表:", lst_copy)original_list = [[1, 2], [3, 4]]
modify_nested_list_shallow(original_list)
print("函数外列表:", original_list)

输出:

函数内列表: [[1, 2, 100], [3, 4]]
函数外列表: [[1, 2, 100], [3, 4]]  # 原嵌套列表被修改

使用深拷贝copy.deepcopy()创建完全独立的副本,即使对象包含嵌套的可变对象:

import copydef modify_list_deep(lst):lst_copy = copy.deepcopy(lst)  # 深拷贝lst_copy[0].append(100)       # 修改嵌套列表print("函数内列表:", lst_copy)original_list = [[1, 2], [3, 4]]
modify_list_deep(original_list)
print("函数外列表:", original_list)

输出:

函数内列表: [[1, 2, 100], [3, 4]]
函数外列表: [[1, 2], [3, 4]]  # 原列表未被修改

何时使用深拷贝

  • 需要完全独立的副本:当函数需要修改传入的可变对象,但又不希望影响外部变量时。
  • 嵌套可变对象:当对象包含多层嵌套的可变结构(如列表的列表、字典的字典)时,必须使用深拷贝。
  • 避免副作用:在并发编程或需要保持数据隔离的场景中,深拷贝能有效防止意外修改。

OK! 


http://www.hkcw.cn/article/FWZLkpsPbk.shtml

相关文章

如何优化微信小程序中渲染带有图片的列表(二进制流存储方式的图片存在本地数据库)

方法一:对列表的获取进行分页处理 实现方法: 前端请求(需要向后端传两个参数,pageIndex是获取第几页是从0开始,pageSize是这一页需要获取多少个数据) 后端接口实现(因为这里是通过参数拼接请求…

电磁器件的“折纸革命“:牛津《Sci. Reports》发布剪纸超材料

01 前沿速递:顶尖团队破解行业难题 近日,牛津大学工程科学系杨云芳、Andrea Vallecchi、Ekaterina Shamonina、Christopher Stevens及游忠教授团队在《Scientific Reports》发表突破性研究,提出一类基于剪纸(Kirigami&#xff0…

【Java学习笔记】接口

接口 应用场景引出 一、接口的介绍 1. 接口的基本结构 interface 接口名{属性抽象方法 }引出关键字:implements 2. 子类实现接口 class a implements 接口名{}3. 接口中的属性说明:属性默认是public static final修饰的 (1)f…

02 APP 自动化-Appium 运行原理详解

环境搭建见 01 APP 自动化-环境搭建 文章目录 一、Appium及Appium自动化测试原理二、Appium 自动化配置项三、常见 ADB 命令四、第一个 app 自动化脚本 一、Appium及Appium自动化测试原理 Appium 跨平台、开源的 app 自动化测试框架,用来测试 app 应用程序&#x…

(1)pytest简介和环境准备

1. pytest简介 pytest是python的一种单元测试框架,与python自带的unittest测试框架类似,但是比unittest框架使用起来更简洁,效率更高。根据pytest的官方网站介绍,它具有如下特点: 非常容易上手,入门简单&a…

同元软控、核动力研究院与华北电力大学产学研联合实训室正式揭牌

2025年5月27日,华北电力大学、苏州同元软控信息技术有限公司(以下简称“同元软控”)、中国核动力研究设计院(以下简称“核动力研究院”)联合实训室揭牌授权仪式暨座谈交流会在华北电力大学召开。华北电力大学教务处处长…

PyTorch中nn.Module详解

直接print(dir(nn.Module)),得到如下内容: 一、模型结构与参数 parameters() 用途:返回模块的所有可训练参数(如权重、偏置)。示例:for param in model.parameters():print(param.shape)named_parameters…

若依项目天气模块

在若依项目里添加了一个天气模块,记录一下过程。 一、功能结构与组件布局 天气模块以卡片形式(el-card)展示,包含以下核心功能: 实时天气:显示当前城市、温度、天气状况(如晴、多云&#xff…

APM32芯得 EP.06 | APM32F407移植uC/OS-III实时操作系统经验分享

《APM32芯得》系列内容为用户使用APM32系列产品的经验总结,均转载自21ic论坛极海半导体专区,全文未作任何修改,未经原文作者授权禁止转载。 最近我开始学习 uC/OS-III 实时操作系统,并着手将其移植到APM32F407 开发板上。在这个过…

图解gpt之注意力机制原理与应用

大家有没有注意到,当序列变长时,比如翻译一篇长文章,或者处理一个长句子,RNN这种编码器就有点力不从心了。它把整个序列信息压缩到一个固定大小的向量里,信息丢失严重,而且很难记住前面的细节,特…

更新密码--二阶注入攻击的原理

1.原理知识: 二阶SQL注入攻击(Second-Order SQL Injection)原理详解 一、基本概念 二阶注入是一种"存储型"SQL注入,攻击流程分为两个阶段: ​​首次输入​​:攻击者将恶意SQL片段存入数据库​…

RFID技术助力托盘运输线革新

RFID技术助力托盘运输线革新 湖北某工厂托盘运输线使用上存在的问题: 1、托盘在运输线上受信息录入时间等问题影响,导致效率低下; 2、原先托盘上粘贴的条码容易污损,并且时常需要更新更换,导致信息录入、出入库等步…

EasyRTC嵌入式音视频通信SDK助力1v1实时音视频通话全场景应用

一、方案概述​ 在数字化通信需求日益增长的今天,EasyRTC作为一款全平台互通的实时视频通话方案,实现了设备与平台间的跨端连接。它支持微信小程序、APP、PC客户端等多端协同,开发者通过该方案可快速搭建1v1实时音视频通信系统,适…

java.io.IOException: ZIP entry size is too large or invalid

java.io.IOException: ZIP entry size is too large or invalid 解决方案&#xff1a;pom.xml添加<nonFilteredFileExtension>xlsx</nonFilteredFileExtension>

vue3 项目配置多语言支持,如何从服务端拿多语言配置

在 Vue3 项目中实现多语言支持并从服务端获取配置&#xff0c;可以使用 Vue I18n 库。在初始化阶段可以发送请求获取多语言配置或者通过本地文件加载json文件的方式&#xff0c;都可以实现。我这里是tauri项目&#xff0c;所以使用的是invoke从tauri端拿到配置文件&#xff0c;…

龙舟竞渡与芯片制造的共通逻辑:华芯邦的文化破局之道

端午节承载着中华民族数千年的精神密码&#xff0c;龙舟最初是古人沟通天地、祈求风调雨顺的仪式载体。战国时期&#xff0c;屈原投江的悲壮故事为端午注入了家国情怀&#xff0c;龙舟竞渡从此兼具纪念英雄与祈福避疫的双重意义。这种文化内核&#xff0c;与深圳市华芯邦“以科…

OS9.【Linux】基本权限(下)

目录 1.默认权限 掩码 修改权限掩码 目录的权限说明 r权限 w权限 x权限 结论 家目录权限 2.共享目录 粘滞位t 承接OS8.【Linux】基本权限(上)文章 1.默认权限 创建用户时拥有者所属组都是该用户,而且对其他人没有任何权限 掩码 新建文件new.txt1和目录folder后…

【容器docker】启动容器kibana报错:“message“:“Error: Cannot find module ‘./logs‘

说明&#xff1a; 1、服务器数据盘挂了&#xff0c;然后将以前的数据用rsync拷贝过去&#xff0c;启动容器kibana服务&#xff0c;报错信息如下图所示&#xff1a; 2、可能是拷贝docker文件夹&#xff0c;有些文件没有拷贝过去&#xff0c;导致无论是给文件夹授权用户kibana或者…

【25-cv-05917】HSP律所代理Le Petit Prince 小王子商标维权案

Le Petit Prince 小王子 案件号&#xff1a;25-cv-05917 立案时间&#xff1a;2025年5月28日 原告&#xff1a;SOCIETE POUR LOEUVRE ET LA MEMOIRE DANTOINE DE SAINT EXUPERY - SUCCESSION DE SAINT EXUPERY-DAGAY 代理律所&#xff1a;HSP 原告介绍 《小王子》&#x…

信创国产化

一、硬件国产化 1. 飞腾E2000Q 二、操作系统国产化 1. 麒麟系统 1.1 麒麟嵌入式支持飞腾E2000Q 1.1.1 启动安装盘制作 1. 下载rufus工具,安装,下载麒麟系统ISO镜像文件。 2. 使用rufus制作启动盘,U盘插入(注先备份数据,会格式化盘符),配置参数如图。 3. 点击…