Python函数的默认参数设计示例详解


Posted in Python onDecember 01, 2019

在Python教程里,针对默认参数,给了一个“重要警告”的例子:

def f(a, L=[]):
  L.append(a)
  return L

print(f(1))
print(f(2))
print(f(3))

默认值只会执行一次,也没说原因。会打印出结果:

[1]
[1, 2]
[1, 2, 3]

因为学的第一门语言是Ruby,所以感觉有些奇怪。 但肯定的是方法f一定储存了变量L。

准备知识:指针

p指向不可变对象,比如数字。则相当于p指针指向了不同的内存地址。

p指向的是可变对象,比如list。list自身的改变,并不会改变list对象自身所在的内存地址。所以p指向的内存地址不变。

>>> p = 1
>>> id(p)
>>> p = p + 1
>>> id(p)
>>> p = 11
>>> id(p)

>>> p = []
>>> id(p)
>>> p.append(11)
>>> id(p)

根本原因

Python函数的参数默认值,是在编译阶段就绑定了。(写代码时就定义了。)

下面是一段从Python Common Gotchas中摘录的原因解释:

Python's default arguments are evaluated once when the function is defined, not each time the function is called (like it is in say, Ruby). This means that if you use a mutable default argument and mutate it, you will and have mutated that object for all future calls to the function as well.

由此可知:

  1. 在运行代码时,运行到函数定义时,默认参数的表达式就被执行了。
  2. 函数调用时,不会再次运行默认参数的表达式。⚠️ 这点和Ruby完全不同。
  3. 由此可知,如果默认参数,指向一个不变对象,例如L = 1。那么在函数调用时,在函数体内对L重新赋值,L其实是一个新的指针, 指向的是一个新的内存地址。而原来默认参数L本身及指向的内存地址,已经储存在最开始编译时的函数定义中。可以用__default__查看。
  4. 如果默认参数指向的是一个可变对象,如list, 那么L.append(a)是对可变对象自身的修改,L指向的内存地址不变。所以每次调用函数,默认参数取出的都是这个内存地址的对象。

第三条,修改上面的例子:

def f(a, L = 1):
  L = a
  print(id(L))
  return L

print("self",id(f.__defaults__[0]))
print(f(1))
print("self",id(f.__defaults__[0]))
print(f(33))
print("self",id(f.__defaults__[0]))

#运行结果:
self 4353170064
1
self 4353170064
33
self 4353170064

默认参数L,在编译阶段就绑定了,储存在__default__内。函数体内的L = a表达式,生成的是新的变量。返回的L是新的变量,和默认参数无关。

第四条,还是上面的例子, 改一下默认参数的类型为可变对象list:

def f(a, L = []):
  L.append(a)
  print(id(L))
  return L
# L = f(1)
print("self",id(f.__defaults__[0]))
print(f(1))
print("self",id(f.__defaults__[0]))
print(f(33))
print("self",id(f.__defaults__[0]))
#返回结果
self 4337586048
[1]
self 4337586048
[1, 33]
self 4337586048

由id号可知,返回的是默认参数自身。

如何避免这个陷阱带来不必要麻烦

def f(a, L = None):
  if L is None:
    L = []
  L.append(a)
  return L

为什么Python要这么设计

 StackOverflow 上争论很多。

Ruby之所以没有这个问题,我想是因为Ruby的def关键字定义了一个封闭作用域,任何数据都必须通过参数传入到方法内,才能用。

和Ruby比,Python参数的作用被大大消弱了。Python的出现晚于Ruby,其创始人肯定参考了Ruby的设计。抛弃了这个设计,选择了类似javascript的函数方式。def定义的函数,执行时是可以接收外部作用域的变量的。

有观点认为:

出于Python编译器的实现方式考虑,函数是一个内部一级对象。而参数默认值是这个对象的属性。在其他任何语言中,对象属性都是在对象创建时做绑定的。因此,函数参数默认值在编译时绑定也就不足为奇了。

本文参考了:http://cenalulu.github.io/python/default-mutable-arguments/#toc1 ,并加入了自己的理解。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
Python多线程编程(四):使用Lock互斥锁
Apr 05 Python
详解python调度框架APScheduler使用
Mar 28 Python
tensorflow创建变量以及根据名称查找变量
Mar 10 Python
python 信息同时输出到控制台与文件的实例讲解
May 11 Python
Python之dict(或对象)与json之间的互相转化实例
Jun 05 Python
Python分布式进程中你会遇到的问题解析
May 28 Python
Django文件存储 自己定制存储系统解析
Aug 02 Python
pytorch逐元素比较tensor大小实例
Jan 03 Python
python 生成器需注意的小问题
Sep 29 Python
python3.9和pycharm的安装教程并创建简单项目的步骤
Feb 03 Python
Django后端按照日期查询的方法教程
Feb 28 Python
Python anaconda安装库命令详解
Oct 16 Python
python线程定时器Timer实现原理解析
Nov 30 #Python
python线程信号量semaphore使用解析
Nov 30 #Python
Python一行代码解决矩阵旋转的问题
Nov 30 #Python
Numpy之将矩阵拉成向量的实例
Nov 30 #Python
numpy np.newaxis 的实用分享
Nov 30 #Python
Python如何使用函数做字典的值
Nov 30 #Python
关于Numpy中的行向量和列向量详解
Nov 30 #Python
You might like
第五节 克隆 [5]
2006/10/09 PHP
PHP更新购物车数量(表单部分/PHP处理部分)
2013/05/03 PHP
PHP使用正则表达式清除超链接文本
2013/11/12 PHP
浅谈PHP变量作用域以及地址引用问题
2013/12/27 PHP
JS类中定义原型方法的两种实现的区别
2007/03/08 Javascript
不用ajax实现点击文字即可编辑的方法
2007/12/16 Javascript
解决jQuery插件tipswindown与hintbox冲突
2010/11/05 Javascript
在Vue中使用highCharts绘制3d饼图的方法
2018/02/08 Javascript
图文介绍Vue父组件向子组件传值
2018/02/17 Javascript
Vue实现带进度条的文件拖动上传功能
2018/02/23 Javascript
layui中table表头样式修改方法
2018/08/15 Javascript
对angularJs中自定义指令replace的属性详解
2018/10/09 Javascript
详细讲解如何创建, 发布自己的 Vue UI 组件库
2019/05/29 Javascript
基于Node.js搭建hexo博客过程详解
2019/06/25 Javascript
JQuery+drag.js上传图片并且实现图片拖曳
2020/11/18 jQuery
python人人网登录应用实例
2014/09/26 Python
在Python中操作字符串之startswith()方法的使用
2015/05/20 Python
Python3 中文文件读写方法
2018/01/23 Python
python操作excel文件并输出txt文件的实例
2018/07/10 Python
详解用python写一个抽奖程序
2019/05/10 Python
Python 串口读写的实现方法
2019/06/12 Python
详解Django关于StreamingHttpResponse与FileResponse文件下载的最优方法
2021/01/07 Python
size?丹麦官网:英国伦敦的球鞋精品店
2019/04/15 全球购物
Under Armour安德玛荷兰官网:美国高端运动科技品牌
2019/07/10 全球购物
请问如下代码执行后a和b的值分别是什么
2016/05/05 面试题
我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串?
2014/03/30 面试题
什么是岗位职责
2013/11/12 职场文书
化学教师自荐信范文
2013/12/28 职场文书
二年级体育教学反思
2014/01/15 职场文书
2014年社区国庆节活动方案
2014/09/16 职场文书
个人授权委托书范本格式
2014/10/12 职场文书
2014年工作总结及2015工作计划
2014/12/12 职场文书
前台接待岗位职责范本
2015/04/03 职场文书
水浒传读书笔记
2015/06/25 职场文书
中学团支部工作总结
2015/08/13 职场文书
vue中div禁止点击事件的实现
2022/04/02 Vue.js