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打造出适合自己的定制化Eclipse IDE
Mar 02 Python
关于Python如何避免循环导入问题详解
Sep 14 Python
基于python3 类的属性、方法、封装、继承实例讲解
Sep 19 Python
Django集成搜索引擎Elasticserach的方法示例
Jun 04 Python
python之信息加密题目详解
Jun 26 Python
flask框架自定义url转换器操作详解
Jan 25 Python
Python多线程实现支付模拟请求过程解析
Apr 21 Python
在Ubuntu 20.04中安装Pycharm 2020.1的图文教程
Apr 30 Python
Python操作word文档插入图片和表格的实例演示
Oct 25 Python
Python中使用aiohttp模拟服务器出现错误问题及解决方法
Oct 31 Python
python实现自定义日志的具体方法
May 28 Python
详解Go语言运用广度优先搜索走迷宫
Jun 23 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
用ADODB来让PHP操作ACCESS数据库的方法
2006/12/31 PHP
php中使用addslashes函数报错问题的解决方法
2013/02/06 PHP
PHP弹出提示框并跳转到新页面即重定向到新页面
2014/01/24 PHP
PHP延迟静态绑定示例分享
2014/06/22 PHP
php身份证号码检查类实例
2015/06/18 PHP
php防止网站被攻击的应急代码
2015/10/21 PHP
laravel实现上传图片,并且制作缩略图,按照日期存放的代码
2019/10/16 PHP
css图片自适应大小
2007/11/28 Javascript
JavaScript中URL编码函数代码
2011/01/11 Javascript
JS 操作符整理[推荐收藏]
2011/11/15 Javascript
JS实用技巧小结(屏蔽错误、div滚动条设置、背景图片位置等)
2016/06/16 Javascript
vue实现ajax滚动下拉加载,同时具有loading效果(推荐)
2017/01/11 Javascript
微信小程序如何获取用户收货地址
2018/11/27 Javascript
JQuery判断radio单选框是否选中并获取值的方法
2019/01/17 jQuery
CountUp.js实现数字滚动增值效果
2019/10/17 Javascript
使用kbone解决Vue项目同时支持小程序问题
2019/11/08 Javascript
vite2.0+vue3移动端项目实战详解
2021/03/03 Vue.js
python文件和目录操作函数小结
2014/07/11 Python
Pandas_cum累积计算和rolling滚动计算的用法详解
2019/07/04 Python
Tensorflow 实现将图像与标签数据转化为tfRecord文件
2020/02/17 Python
HTML5 拖放(Drag 和 Drop)详解与实例代码
2017/09/14 HTML / CSS
摩托车和ATV零件、配件和服装的首选在线零售商:MotoSport
2017/12/22 全球购物
Piercing Pagoda官网:耳环、戒指、项链、手链等
2020/09/28 全球购物
编写用C语言实现的求n阶阶乘问题的递归算法
2014/10/21 面试题
最新党员思想汇报
2014/01/01 职场文书
阿德的梦教学反思
2014/02/06 职场文书
表演方阵解说词
2014/02/08 职场文书
幼儿园毕业家长感言
2014/02/10 职场文书
规划编制实施方案
2014/03/15 职场文书
《北大荒的秋天》教学反思
2014/04/14 职场文书
学生安全责任书
2014/04/15 职场文书
小学优秀教育工作者事迹材料
2014/05/09 职场文书
出国留学英文自荐信
2015/03/25 职场文书
委托收款证明
2015/06/23 职场文书
Python数据分析入门之数据读取与存储
2021/05/13 Python
weblogic服务建立数据源连接测试更新mysql驱动包的问题及解决方法
2022/01/22 MySQL