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 sys模块sys.path使用方法示例
Dec 04 Python
简单介绍Python中的RSS处理
Apr 13 Python
Python基于pygame实现图片代替鼠标移动效果
Nov 11 Python
网红编程语言Python将纳入高考你怎么看?
Jun 07 Python
Flask框架WTForm表单用法示例
Jul 20 Python
python将一组数分成每3个一组的实例
Nov 14 Python
对Python 两大环境管理神器 pyenv 和 virtualenv详解
Dec 31 Python
linux查找当前python解释器的位置方法
Feb 20 Python
python函数的作用域及关键字详解
Aug 20 Python
Python调用系统命令os.system()和os.popen()的实现
Dec 31 Python
Python机器学习工具scikit-learn的使用笔记
Jan 28 Python
Python爬取科目四考试题库的方法实现
Mar 30 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
php学习之 数组声明
2011/06/09 PHP
自己写了一个php检测文件编码的函数
2014/04/21 PHP
Javascript学习笔记 delete运算符
2011/09/13 Javascript
JS判断对象是否存在的10种方法总结
2013/12/23 Javascript
table对象中的insertRow与deleteRow使用示例
2014/01/26 Javascript
使用jquery实现IE下按backspace相当于返回操作
2014/03/18 Javascript
javascript清空table表格的方法
2015/05/14 Javascript
javascript实现粘贴qq截图功能(clipboardData)
2016/05/29 Javascript
Angularjs中使用轮播图指令swiper
2017/05/30 Javascript
JS简单实现父子窗口传值功能示例【未使用iframe框架】
2017/09/20 Javascript
详解让sublime text3支持Vue语法高亮显示的示例
2017/09/29 Javascript
p5.js入门教程之鼠标交互的示例
2018/03/16 Javascript
Phaser.js实现简单的跑酷游戏附源码下载
2018/10/26 Javascript
从0到1搭建Element的后台框架的方法步骤
2019/04/10 Javascript
vue-cli —— 如何局部修改Element样式
2020/10/22 Javascript
[49:08]FNATIC vs Infamous 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/18 DOTA
Python本地与全局命名空间用法实例
2015/06/16 Python
python字符串常用方法
2018/06/14 Python
Python实现Linux监控的方法
2019/05/16 Python
Django错误:TypeError at / 'bool' object is not callable解决
2019/08/16 Python
Python3的unicode编码转换成中文的问题及解决方案
2019/12/10 Python
Python xlwt模块使用代码实例
2020/06/10 Python
Python fileinput模块如何逐行读取多个文件
2020/10/05 Python
Pycharm 跳转回之前所在页面的操作
2021/02/05 Python
CSS3 特效范例整理
2011/08/22 HTML / CSS
德国网上花店:Valentins
2018/08/15 全球购物
英国在线潜水商店:Simply Scuba
2019/03/25 全球购物
湖南卫视在线视频媒体平台:芒果TV
2019/10/30 全球购物
Java编程面试题
2016/04/04 面试题
一套Delphi的笔试题一
2016/02/14 面试题
施工班组长岗位职责
2014/01/05 职场文书
广告宣传策划方案
2014/05/21 职场文书
甜品店创业计划书
2014/08/14 职场文书
2015年餐厅服务员工作总结
2015/04/23 职场文书
瞿秋白纪念馆观后感
2015/06/10 职场文书
python中if和elif的区别介绍
2021/11/07 Python