当前位置: 首页 > news >正文

如何避免Python中默认参数带来的陷阱

Python编程中,我们有时会给函数或方法提供默认参数。然而,这种做法在某些情况下可能会导致意想不到的行为,尤其是当默认参数是可变对象(例如列表、字典或类实例对象)时。本文将通过几个具体的例子来解释这个问题,并提供解决方案。

问题示例

示例一:HauntedBus

首先,考虑以下HauntedBus类:

class HauntedBus:"""A bus model haunted by ghost passengers"""def __init__(self, passengers=[]):self.passengers = passengersdef pick(self, name):self.passengers.append(name)def drop(self, name):self.passengers.remove(name)

在这个类中,passengers参数有一个默认值[]。现在,我们创建两个HauntedBus实例,并向第一个实例添加乘客:

bus1 = HauntedBus()
bus1.pick("小明")
bus1.pick("小红")
print(bus1.passengers)  # 输出: ['小明', '小红']bus2 = HauntedBus()
print(bus2.passengers)  # 输出: ['小明', '小红']

你可能会预期bus2的乘客列表应该是空的,但实际输出表明它包含了bus1的乘客。这是为什么呢?

示例二:使用字典作为默认参数

def add_entry(key, value, dictionary={}):dictionary[key] = valuereturn dictionaryd1 = add_entry('name', 'Alice')
print(d1)  # 输出: {'name': 'Alice'}d2 = add_entry('age', 30)
print(d2)  # 输出: {'name': 'Alice', 'age': 30}

在这个例子中,dictionary参数的默认值是一个空字典。第一次调用add_entry函数时,向字典中添加了键值对'name': 'Alice'。第二次调用时,字典中已经有了之前添加的键值对,所以又添加了键值对'age': 30。发现两次调用共享了同一个字典。

示例三:使用自定义类对象作为默认参数

class DefaultObject:def __init__(self):self.data = []print("DefaultObject Init")def add_to_default(obj=DefaultObject()):obj.data.append(1)return obj.dataresult1 = add_to_default()
print(result1)  # 输出: [1]result2 = add_to_default()
print(result2)  # 输出: [1, 1]

在这个例子中,obj参数的默认值是一个DefaultObject实例。第一次调用add_to_default函数时,向data列表中添加了数字1。第二次调用时,data列表中已经有了一个1,所以又添加了一个1。发现两次调用共享了同一个DefaultObject实例。

原因解析

在Python中,默认参数是在函数定义的时候只初始化一次的,而不是每次调用函数时重新初始化。如果默认参数是一个可变类型/对象,那么后续对这个函数的调用将共享同一个默认参数对象。

解决方案

为了解决这个问题,我们可以使用None作为默认参数值,并在函数内部进行检查和初始化。这样每次创建新实例时都会创建一个新的可变对象,从而避免不同实例或调用之间共享同一个默认参数对象。

修复后的HauntedBus

class HauntedBus:"""A bus model haunted by ghost passengers"""def __init__(self, passengers=None):if passengers is None:passengers = []self.passengers = passengersdef pick(self, name):self.passengers.append(name)def drop(self, name):self.passengers.remove(name)

现在,我们再次创建两个HauntedBus实例并测试:

bus1 = HauntedBus()
bus1.pick("小明")
bus1.pick("小红")
print(bus1.passengers)  # 输出: ['小明', '小红']bus2 = HauntedBus()
print(bus2.passengers)  # 输出: []

这样,每个实例都有自己独立的乘客列表,不会相互影响。

修复后的add_entry函数

def add_entry(key, value, dictionary=None):if dictionary is None:dictionary = {}dictionary[key] = valuereturn dictionaryd1 = add_entry('name', 'Alice')
print(d1)  # 输出: {'name': 'Alice'}d2 = add_entry('age', 30)
print(d2)  # 输出: {'age': 30}

通过将默认参数设置为None并在函数内部进行初始化,每次调用add_entry函数时都会创建一个新的字典,从而避免不同调用之间共享同一个字典。

修复后的add_to_default函数

class DefaultObject:def __init__(self):self.data = []print("DefaultObject Init")def add_to_default(obj=None):if obj is None:obj = DefaultObject()obj.data.append(1)return obj.dataresult1 = add_to_default()
print(result1)  # 输出: [1]result2 = add_to_default()
print(result2)  # 输出: [1]

通过将默认参数设置为None并在函数内部进行初始化,每次调用add_to_default函数时都会创建一个新的DefaultObject实例,从而避免不同调用之间共享同一个实例。

结论

在Python中使用默认参数时,尤其是可变对象,必须小心处理。通过使用None作为默认值并在函数内部进行初始化,可以避免默认参数带来的潜在陷阱。希望这些例子能帮助你理解并避免类似的问题。

作者:Black_Boy
链接:https://juejin.cn/post/7376889083211300905

相关文章:

如何避免Python中默认参数带来的陷阱

Python编程中,我们有时会给函数或方法提供默认参数。然而,这种做法在某些情况下可能会导致意想不到的行为,尤其是当默认参数是可变对象(例如列表、字典或类实例对象)时。本文将通过几个具体的例子来解释这个问题&#…...

代码随想录算法训练营第五十天|198.打家劫舍、213.打家劫舍II、337.打家劫舍III

代码随想录算法训练营第五十天 198.打家劫舍 题目链接:198.打家劫舍 确定dp数组以及下标的含义:dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]。确定递推公式:max(dp[i - 1],…...

VB.net 进行CAD二次开发(二)

利用参考文献2,添加面板 执行treeControl New UCTreeView()时报一个错误: 用户代码未处理 System.ArgumentException HResult-2147024809 Message控件不支持透明的背景色。 SourceSystem.Windows.Forms StackTrace: 在 System.Windows…...

安徽某高校数据挖掘作业6

1 根据附件中year文件,编辑Python程序绘制年销售总额分布条形图和年净利润分布条形图,附Python程序和图像。 2 根据附件中quarter和quarter_b文件,编辑Python程序绘制2018—2020年销售额和净利润折线图,附Python程序和图像。 3 …...

CMakeLists.txt和Package.xml

CMakeLists.txt和Package.xml CMakeLists.txt 总览 CMakeLists.txt 是用于定义如何构建 ROS (Robot Operating System) 包的 CMake 脚本文件。CMake 是一个跨平台的构建系统,用于自动化编译过程。在 ROS 中,CMakeLists.txt 文件指定了如何编译代码和链…...

Debian常用命令详解

Debian常用命令详解 Debian是一个流行的Linux发行版,它以其稳定性、强大的包管理系统和丰富的软件仓库而著称。对于Debian用户来说,掌握一些常用的命令行工具和命令是日常系统管理和维护的基础。下面,我们将介绍一些Debian系统中常用的命令。…...

代码随想录算法训练营day29|491.递增子序列、46.全排列、47.全排列II

递增子序列 491. 非递减子序列 - 力扣(LeetCode) 非递减子序列,则答案的子集中,需保持下一个元素大于等于前一个元素的顺序,由于题目中指出,所有的子序列长度需大于等于2,考虑当条件为path.siz…...

【ARM Cache 与 MMU 系列文章 7.8 – ARMv8/v9 MMU Table 表分配原理及其代码实现 2】

请阅读【ARM Cache 及 MMU/MPU 系列文章专栏导读】 及【嵌入式开发学习必备专栏】 文章目录 MMU Table 表分配原理及其代码实现MMU Table 分配代码实现MMU Table 表分配原理及其代码实现 在做映射的时候所映射的地址范围最大只能是某一级 level table 中 entry 所能支持的最大…...

SAP PP学习笔记17 - MTS(Make-to-Stock) 按库存生产(策略70)

上几章讲了几种策略,策略10,11,30,40。 SAP PP学习笔记14 - MTS(Make-to-Stock) 按库存生产(策略10),以及生产计划的概要-CSDN博客 SAP PP学习笔记15 - MTS(Make-to-St…...

网页音频提取在线工具有哪些 网页音频提取在线工具下载

别再到处去借会员账号啦。教你一招,无视版权和地区限制,直接下载网页中的音频文件。没有复杂的操作步骤,也不用学习任何代码。只要是网页中播放的音频文件,都可以把它下载到本地保存。 一、网页音频提取在线工具有哪些 市面上的…...

【ARM Cache 系列文章 2.1 -- Cache PoP 及 PoDP 介绍】

请阅读【ARM Cache 及 MMU/MPU 系列文章专栏导读】 及【嵌入式开发学习必备专栏】 文章目录 PoP 及 PoDPCache PoDPCache PoP应用和影响PoP 及 PoDP Cache PoDP 点对深度持久性(Point of Deep Persistence, PoDP)是内存系统中的一个点,在该点达到的任何写操作即使在系统供电…...

一文了解JVM面试篇(上)

Java内存区域 1、如何解释 Java 堆空间及 GC? 当通过 Java 命令启动 Java 进程的时候,会为它分配内存。内存的一部分用于创建 堆空间,当程序中创建对象的时候,就从对空间中分配内存。GC 是 JVM 内部的一 个进程,回收无效对象的内存用于将来的分配。 2、JVM 的主要组成…...

C#WPF控件Textbox绑定浮点型数据限制小数位方法

本文讲解C#WPF控件Textbox绑定浮点型数据限制小数位方法。 XAML中,使用StringFormat来格式化TextBox的文本 <Window x:Class="WpfApp.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.m…...

mysql引入表名称的注意事项

1、遇到问题 mapper中的文件是这样的 解析出来的sql是这样的 sql显示为&#xff1a;select * from ‘tableName’ 2、解决方法 mapper文件种使用${tableName}而不是#{tableName}...

C语言数据结构快速排序的非递归、归并排序、归并排序的非递归等的介绍

文章目录 前言一、快速排序非递归二、归并排序五、归并排序非递归总结 前言 C语言数据结构快速排序的非递归、归并排序、归并排序的非递归等的介绍 一、快速排序非递归 快速排序非递归的定义 快速排序非递归&#xff0c;需要使用栈来实现。将左右下标分别push到栈中。在栈为…...

学生成绩管理系统(大一大作业)

功能 实现添加&#xff0c;排序&#xff0c;修改&#xff0c;保存等功能 库函数 #include<stdio.h> #include<stdlib.h> #include<windows.h> #include<string.h> 头文件 #define functioncreate(major) void major##compare(mana mn){\int i,j,s…...

数据结构:模拟栈

数据结构&#xff1a;模拟栈 题目描述参考代码 题目描述 输入样例 10 push 5 query push 6 pop query pop empty push 4 query empty输出样例 5 5 YES 4 NO参考代码 #include <iostream>using namespace std;const int N 1000010;int m, x; int q[N]; string op; int…...

02-2.3.6 顺序表和链表的比较

喜欢《数据结构》部分笔记的小伙伴可以订阅专栏&#xff0c;今后还会不断更新。&#x1f9d1;‍&#x1f4bb; 此外&#xff0c;《程序员必备技能》专栏和《程序员必备工具》专栏&#xff08;该专栏暂未开设&#xff09;日后会逐步更新&#xff0c;感兴趣的小伙伴可以点一下订阅…...

C++ : 模板初阶

标题&#xff1a;C : 模板初阶 水墨不写bug 正文开始&#xff1a; C语言的问题 &#xff1a; 写不完的swap函数 在学习C语言时&#xff0c;我们有一个经常使用的函数swap函数&#xff0c;它可以将两个对象的值交换。 我们通常这样实现它&#xff1a; void swap(int t1,int t2)…...

FFA-Net:用于单图像去雾的特征融合注意力网络

摘要 论文链接&#xff1a;https://arxiv.org/pdf/1911.07559v2 在这篇论文中&#xff0c;我们提出了一种端到端的特征融合注意力网络&#xff08;FFA-Net&#xff09;来直接恢复无雾图像。FFA-Net架构由三个关键组件组成&#xff1a; 一种新颖的特征注意力&#xff08;FA&…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

C++.OpenGL (10/64)基础光照(Basic Lighting)

基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...