百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Python参数传递:传递可变对象详解

itomcoil 2025-06-09 22:38 6 浏览

在Python中,理解参数传递机制对于编写正确的代码至关重要,特别是当传递可变对象时。下面我将详细分析传递可变对象的行为和内存机制。

1. Python的参数传递本质



Python中的参数传递是"对象引用传递"(或称为"按共享传递")。这意味着:

  • 传递的是对象的引用(内存地址),而不是对象本身的副本
  • 对于不可变对象(如数字、字符串、元组),函数内修改不会影响原始对象
  • 对于可变对象(如列表、字典、集合),函数内修改会影响原始对象

2. 传递可变对象的内存分析



示例1

:修改可变对象

def modify_list(lst):
    lst.append(4)  # 直接修改原列表
    print(f"函数内列表: {lst}, 内存地址: {id(lst)}")

my_list = [1, 2, 3]
print(f"原始列表: {my_list}, 内存地址: {id(my_list)}")
modify_list(my_list)
print(f"修改后列表: {my_list}, 内存地址: {id(my_list)}")

输出结果

原始列表: [1, 2, 3], 内存地址: 140235678456000
函数内列表: [1, 2, 3, 4], 内存地址: 140235678456000
修改后列表: [1, 2, 3, 4], 内存地址: 140235678456000

内存示意图

调用前:
my_list → [1, 2, 3](内存地址140235678456000)

调用时:
lst参数接收到my_list的引用,两者指向同一对象

修改后:
my_list和lst都指向 [1, 2, 3, 4](内存地址不变)

示例2:重新赋值可变对象

def reassign_list(lst):
    lst = [4, 5, 6]  # 创建新列表并赋值给局部变量lst
    print(f"函数内新列表: {lst}, 内存地址: {id(lst)}")

my_list = [1, 2, 3]
print(f"原始列表: {my_list}, 内存地址: {id(my_list)}")
reassign_list(my_list)
print(f"调用后列表: {my_list}, 内存地址: {id(my_list)}")

输出结果

原始列表: [1, 2, 3], 内存地址: 140235678456000
函数内新列表: [4, 5, 6], 内存地址: 140235678456128
调用后列表: [1, 2, 3], 内存地址: 140235678456000

关键区别

  • append()等方法是原地修改,会影响原始对象
  • 重新赋值(=)是创建新对象,不会影响原始对象

3. 常见可变对象传递场景

场景1:列表作为参数

def process_items(items):
    items.append('new')  # 修改会影响调用方
    items[0] = 'modified'  # 修改会影响调用方

shopping_list = ['apple', 'banana']
process_items(shopping_list)
print(shopping_list)  # ['modified', 'banana', 'new']

场景2:字典作为参数

def update_user(user):
    user['age'] += 1  # 修改会影响调用方
    user.setdefault('status', 'active')

user_data = {'name': 'Alice', 'age': 30}
update_user(user_data)
print(user_data)  # {'name': 'Alice', 'age': 31, 'status': 'active'}

场景3:集合作为参数

def add_to_set(s):
    s.add(4)  # 修改会影响调用方

numbers = {1, 2, 3}
add_to_set(numbers)
print(numbers)  # {1, 2, 3, 4}

4. 如何避免意外修改原始对象

如果不想函数内修改影响原始对象,可以:

方法1:显式创建副本

def safe_modify(lst):
    lst = lst.copy()  # 创建浅拷贝
    lst.append(4)
    return lst

original = [1, 2, 3]
new = safe_modify(original)
print(original)  # [1, 2, 3] (未改变)
print(new)      # [1, 2, 3, 4]

方法2:传递不可变副本

def safe_modify_dict(d):
    d = dict(d)  # 创建新字典
    d['new'] = 'value'
    return d

original_dict = {'a': 1}
new_dict = safe_modify_dict(original_dict)
print(original_dict)  # {'a': 1}
print(new_dict)      # {'a': 1, 'new': 'value'}

方法3:使用不可变类型

def process_data(data):
    # 元组是不可变的,无法修改
    data = data + (4,)  # 这会创建新元组
    return data

original_tuple = (1, 2, 3)
new_tuple = process_data(original_tuple)
print(original_tuple)  # (1, 2, 3)
print(new_tuple)      # (1, 2, 3, 4)

5. 深浅拷贝对参数传递的影响

对于嵌套的可变对象,需要注意深浅拷贝的区别:

import copy

def modify_nested(lst):
    # 浅拷贝:只拷贝最外层
    lst = copy.copy(lst)
    lst.append(3)           # 不影响原列表
    lst[0][0] = 'modified'  # 会影响原列表的内部列表
    
original = [[1, 2], ['a', 'b']]
modify_nested(original)
print(original)  # [['modified', 2], ['a', 'b']]

如果需要完全独立的副本,应该使用深拷贝:

def truly_independent_modify(lst):
    lst = copy.deepcopy(lst)
    lst[0][0] = 'changed'
    return lst

original = [[1, 2], ['a', 'b']]
new = truly_independent_modify(original)
print(original)  # [[1, 2], ['a', 'b']] (完全未改变)
print(new)      # [['changed', 2], ['a', 'b']]

6. 最佳实践建议

  1. 明确函数契约:在文档中说明函数是否会修改输入参数
  2. 防御性编程:如果不希望修改输入,函数内部应先创建副本
  3. 优先返回新对象:而不是修改输入参数,除非有特殊需求
  4. 注意嵌套结构:对于复杂数据结构,考虑使用deepcopy
  5. 使用类型提示:明确参数和返回值的类型
from typing import List, Dict

def process_data(data: List[Dict]) -> List[Dict]:
    """处理数据并返回新列表,不修改输入"""
    data = copy.deepcopy(data)
    # 处理逻辑...
    return data

理解Python中可变对象的传递机制,可以帮助你避免许多常见的错误,特别是涉及数据意外修改的问题。

相关推荐

麦克斯韦方程高斯-博内-陈定理嘉当外微分斯托克斯方程之间的联系

一、麦克斯韦方程的外微分形式1.电磁场张量的几何表示在微分几何中,电磁场由2-形式F描述,称为电磁场强形式:其中E为电场,B为磁场,dt为时间坐标。2.麦克斯韦方程的微分形式第二式(无...

机器视觉(四):空域图像增强(常见的空域图像增强处理方法有哪些)

一、图像处理技术概述1.定义对原始获取图像进行一系列的运算处理,称为图像处理。图像处理是机器视觉技术的方法基础,包括图像增强、边缘提取、图像分割、形态学处理、图像投影、配准定位和图像特征提取等方法。2...

基于机器视觉的安全气囊检测系统研究

21世纪以来,汽车行业发展迅猛。安全气囊作为汽车的安全辅助工具,它的出现大大降低了死亡率,因而必须确保其尺寸精确,并能正常使用。对安全气囊传统的检测方法主要是借助千分表、轮廓仪等工具进行人工抽检[...

高斯泼溅综合指南(高斯pp)

高斯泼溅(GaussianSplatting)是一种表示3D场景和渲染新视图的方法,在“实时辐射场渲染的3D高斯泼溅”中引入。它可以被认为是NeRF类模型的替代品,就像当年的NeRF...

基于DSP和ARM的电气化铁路接触线覆冰监测系统设计

张学武(中铁第一勘察设计院集团有限公司,陕西西安710043)摘要:为预防接触线上的冰害事故以及为除冰融冰提供支持,设计了集成视频传输、覆冰厚度测量、导线温度测量、现场微气象采集的接触线覆冰监测系...

OpenCV 线性滤波(opencv 线性拟合)

OpenCV线性滤波线性滤波图像滤波与滤波器图像滤波,指的是在尽量保留图像特征的条件下对目标图像得噪声进行抑制,是图像处理当中不可缺少的部分。平滑录播室低频增强的空间域滤波技术,它的目的有两类:一类...

几种典型的图像去噪算法总结(图像去噪的定义)

(一)高斯低通滤波去噪高斯低通滤波器(GaussianLowPassFilter)是一类传递函数为高斯函数的线性平滑滤波器。又由于高斯函数是正态分布的密度函数。因此高斯低通滤波器对于去除服从正态...

深度解析卷积:从原理到应用(卷积到底是什么)

一、卷积的基本概念卷积是一种在数学领域尤其是分析数学中占据重要地位的运算方式。它通过两个函数和来生成第三个函数。设和是上的两个可积函数,卷积的积分表达式为:。在离散情况下,卷积定义为。从...

基于混合高斯模型的物体成分拟合方法

郎波,樊一娜,黄静,王鹏(北京师范大学珠海分校信息技术学院,广东珠海519087)为了寻求代价更小、效率更高、适应性更强的图像原型表征方法,借鉴成分识别理论的观点,设计出一种更符合人类认知原...

30天Python 入门到精通(python入门到精通书籍)

以下是一个为期30天的Python入门到精通学习课程,专为零基础新手设计。课程从基础语法开始,逐步深入到面向对象编程、数据处理,最后实现运行简单的大语言模型(如基于HuggingFace...

Python 最好用的 8 个 VS Code 扩展

1.PythonextensionforVisualStudioCode这个扩展是由微软官方提供的,支持但不仅限于以下功能:通过Pylint或Flake8支持代码检查在VSCo...

Textual:为 Python 增加漂亮的文本用户界面(TUI)

导读:如果你的代码是用Python编写的,你应该使用Textual来帮助你编写TUI(文本用户界面)。快速入门使用TextualPython在Linux上有像TkInterdocs...

pytest框架进阶自学系列 | 常用插件的使用

书籍来源:房荔枝梁丽丽《pytest框架与自动化测试应用》一边学习一边整理老师的课程内容及实验笔记,并与大家分享,侵权即删,谢谢支持!附上汇总贴:pytest框架进阶自学系列|汇总_热爱编程的通...

ShapeNet数据集及dataset代码分析

1.数据集简介ShpaeNet是点云中一个比较常见的数据集,它能够完成部件分割任务,即部件知道这个点云数据大的分割,还要将它的小部件进行分割。它总共包括十六个大的类别,每个大的类别有可以分成若干个小类...

「教程」5 分钟带你入门 kivy(怎么学kp)

原创:星安果AirPythonkivy语言通过编写界面UI,然后利用Python定义一些业务逻辑,可以移植很多功能模块到移动端直接执行。下面对kivy常见用法做一个汇总。1、什么是...