不要使用可变对象作为函数的默认参数例如 list,dict,因为def是一个可执行语句,只有def执行的时候才会计算默认默认参数的值,所以使用默认参数会造成函数执行的时候一直在使用同一个对象,引起bug。
基本原理
在 Python 源码中,我们使用def来定义函数或者方法。在其他语言中,类似的东西往往只是一一个语法声明关键字,但def却是一个可执行的指令。Python代码执行的时候先会使用 compile 将其编译成 PyCodeObject.
PyCodeObject 本质上依然是一种静态源代码,只不过以字节码方式存储,因为它面向虚拟机。因此 Code 关注的是如何执行这些字节码,比如栈空间大小,各种常量变量符号列表,以及字节码与源码行号的对应关系等等。
PyFunctionObject 是运行期产生的。它提供一个动态环境,让 PyCodeObject 与运行环境关联起来。同时为函数调用提供一系列的上下文属性,诸如所在模块、全局名字空间、参数默认值等等。这是def语句执行的时候干的活。
PyFunctionObject 让函数面向逻辑,而不仅仅是虚拟机。PyFunctionObject 和 PyCodeObject 组合起来才是一个完整的函数。
下文翻译了一篇文章,有一些很好的例子。但是由于水平有限,有些不会翻译或者有些翻译有误,敬请谅解。如果有任何问题请发邮件到 acmerfight圈gmail.com,感激不尽
主要参考资料 书籍:《深入Python编程》 大牛:shell 和 Topsky
原文链接
Python对于函数中默认参数的处理往往会给新手造成困扰(但是通常只有一次)。
当你使用“可变”的对象作为函数中作为默认参数时会往往引起问题。因为在这种情况下参数可以在不创建新对象的情况下进行修改,例如 list dict。
>>> def function(data=[]):
... data.append(1)
... return data
...
>>> function()
[1]
>>> function()
[1, 1]
>>> function()
[1, 1, 1]
像你所看到的那样,list变得越来越长。如果你仔细地查看这个list。你会发现list一直是同一个对象。
>>> id(function())
12516768
>>> id(function())
12516768
>>> id(function())
12516768
原因很简单: 在每次函数调用的时候,函数一直再使用同一个list对象。这么使用引起的变化,非常“sticky”。
为什么会发生这种情况?
当且仅当默认参数所在的“def”语句执行的时候,默认参数才会进行计算。请看文档描述
http://docs.python.org/ref/function.html
的相关部分。
"def"是Python中的可执行语句,默认参数在"def"的语句环境里被计算。如果你执行了"def"语句多次,每次它都将会创建一个新的函数对象。接下来我们将看到例子。
用什么来代替?
像其他人所提到的那样,用一个占位符来替代可以修改的默认值。None
def myfunc(value=None):
if value is None:
value = []
# modify value here
如果你想要处理任意类型的对象,可以使用sentinel
sentinel = object()
def myfunc(value=sentinel):
if value is sentinel:
value = expression
# use/modify value here
在比较老的代码中,written before “object” was introduced,你有时会看到
sentinel = ['placeholder']
译者注:太水,真的不知道怎么翻译了。我说下我的理解 有时逻辑上可能需要传递一个None,而你的默认值可能又不是None,而且还刚好是个列表,列表不
可以写在默认值位置,所以你需要占位符,但是用None,你又不知道是不是调用者传递过来的那个
正确地使用可变参数
最后需要注意的是一些高深的Python代码经常会利用这个机制的优势;举个例子,如果在一个循环里创建一些UI上的按钮,你可能会尝试这样去做:
for i in range(10):
def callback():
print "clicked button", i
UI.Button("button %s" % i, callback)
但是你却发现callback打印出相同的数字(在这个情况下很可能是9)。原因是Python的嵌套作用域只是绑定变量,而不是绑定数值的,所以callback只看到了变量i绑定的最后一个数值。为了避免这种情况,使用显示绑定。
for i in range(10):
def callback(i=i):
print "clicked button", i
UI.Button("button %s" % i, callback)
i=i把callback的参数i(一个局部变量)绑定到了当前外部的i变量的数值上。(译者注:如果不理解这个例子,请看http://stackoverflow.com/questions/233673/lexical-closures-in-python)
另外的两个用途local caches/memoization
def calculate(a, b, c, memo={}):
try:
value = memo[a, b, c] # return already calculated value
except KeyError:
value = heavy_calculation(a, b, c)
memo[a, b, c] = value # update the memo dictionary
return value
(对一些递归算法非常好用)
对高度优化的代码而言, 会使用局部变量绑全局的变量:
import math
def this_one_must_be_fast(x, sin=math.sin, cos=math.cos):
...
这是如何工作的?
当Python执行一条def语句时, 它会使用已经准备好的东西(包括函数的代码对象和函数的上下文属性),创建了一个新的函数对象。同时,计算了函数的默认参数值。
不同的组件像函数对象的属性一样可以使用。上文用到的'function'
>>> function.func_name
'function'
>>> function.func_code
", line 1>
>>> function.func_defaults
([1, 1, 1],)
>>> function.func_globals
{'function': ,
'__builtins__': ,
'__name__': '__main__', '__doc__': None}
这样你可以访问默认参数,你甚至可以修改它。
>>> function.func_defaults[0][:] = []
>>> function()
[1]
>>> function.func_defaults
([1],)
然而我不推荐你平时这么使用。
另一个重置默认参数的方法是重新执行相同的def语句,Python将会和代码对象创建一个新的函数对象,并计算默认参数,并且把新创建的函数对象赋值给了和上次相同的变量。但是再次强调,只有你清晰地知道在做什么的情况下你才能这么做。
相关推荐
Python 函数习题及答案 -- 中文 本章节主要讲解了 Python ...本章节讲解了 Python 函数的基本概念和应用,包括函数的定义、函数的调用、函数的参数、函数的返回值等,同时也探讨了变量的范围和按值传递参数等问题。
在Python中,函数的参数有四种: 必需参数 关键字参数 默认参数 不定长参数 必需参数是在函数调用时,须以正确的顺序传入,且调用时的数量必须和声明时的一样参数。 实例:定义一个printme函数,并定义一个必需参数...
在调用函数的过程中参数是如何被解析先看第一个问题,在python中函数参数的定义主要有四种方式: 1.F(arg1,arg2,…) 这 是最常见的定义方式,一个函数可以定义任意个参数,每个参数间用逗号分割,用这种方式定义的...
本文实例讲述了Python中函数及默认参数的定义与调用操作。分享给大家供大家参考,具体如下: #coding=utf8 ''''' Python中的函数使用小括号调用。函数在调用之前必须先定义。 如果函数中没有return语句,就会自动...
一般来说,将参数值变化小的设置为默认参数。 python标准库实践 python内建函数: 函数签名可以看出,使用print(‘hello python’)这样的简单调用的打印语句,实际上传入了许多默认值,默认参数使得函数的调用变得...
主要介绍了深入理解python中函数传递参数是值传递还是引用传递,涉及具体代码示例,具有一定参考价值,需要的朋友可以了解下。
python中factor函数_Python基础教程 6.4.5 参数收集的逆过程 假设有如下函数: def add(x,y): return x+y ⽐如说有个包含由两个相加的数字组成的元组: params = (1,2) 使⽤*运算符对参数进⾏"分配",不过是在调⽤⽽...
本文实例讲述了Python中函数参数调用方式。分享给大家供大家参考,具体如下: Python中函数的参数是很灵活的,下面分四种情况进行说明。 (1) fun(arg1, arg2, …) 这是最常见的方式,也是和其它语言类似的方式 下面...
主要介绍了深入了解Python函数中参数是传值还是传引用,在 C/C++ 中,传值和传引用是函数参数传递的两种方式,在Python中参数是如何传递的,需要的朋友可以参考下
一、默认参数 python为了简化函数的调用,提供了默认参数机制: ...一般来说,将参数值变化小的设置为默认参数。 python标准库实践 python内建函数: print(*objects, sep=' ', end='\\n', file=sys
对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么样的值就够了,函数内部的复杂逻辑被封装起来,调用者无需了解。 Python的函数定义非常简单,但灵活度却非常大。除了正常定义的必选参数外...
大家在看Python中各个模块的函数时,经常会看到类似如下的情况: def plotxxx(a, b, *args, **kwargs): pass 关于其中的参数部分“*args, **kwargs”,许多人不是特别清晰,看到了许多介绍的文章,也觉得没有说的...
不要使用可变对象作为函数的默认参数例如 list,dict,因为def是一个可执行语句,只有def执行的时候才会计算默认默认参数的值,所以使用默认参数会造成函数执行的时候一直在使用同一个对象,引起bug。 基本原理 在 ...
在 Python 中,函数的定义语法为 `def function_name(arg1,arg2[,...]): statement [return value]`,其中 `function_name` 是函数名,`arg1,arg2,...` 是参数列表,`statement` 是函数体,`return value` 是返回值...
定义一个数getMax()函数,返回三个数(从键盘输入的整数)中最大值。 def getMax(): num1=int(input("请在键盘上输入第1个整数:")) num2=int(input("请在键盘上输入第2个整数:")) num3=int(input("请在键盘上...
对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么样的值就够了,函数内部的复杂逻辑被封装起来,调用者无需了解。 Python的函数定义非常简单,但灵活度却非常大。除了正常定义的必选参数外...
实参:函数调用时 的参数值 局部变量 函数内部声明的变量; 函数外具有相同名称的其他变量没有任何关系; 变量名称对于函数来说是局部的; “作用域”:所有变量的作用域是它们被定义的块,从它们的名称被定义的那...
python中cos函数怎么⽤_Pythoncos()函数使⽤⽅法⼊门教 程基础教程 Python cos() 函数 描述 cos() 返回x的弧度的余弦值。 语法 以下是 cos() ⽅法的语法: import math math.cos(x) 注意:cos()是不能直接访问的,...
Python 的函数参数主要包含以下几种: 必选参数; 默认参数; 可变参数; 关键字参数。 必选参数也叫位置参数,是函数中最常用的参数。必选参数就是在调用函数的时候必须指定参数值。例如: 如果调用plus函数...