不要使用可变对象作为函数的默认参数例如 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中函数参数的四种类型:必需参数、关键字参数、默认参数和不定长参数。 1. **必需参数**: 必需参数是在定义函数时明确指定的参数,调用函数时必须按顺序提供这些参数。如果缺少任何一个...
### Python中函数参数设置及使用的深入解析 #### 一、参数和共享引用 在Python中,函数参数通过赋值的方式传递。当函数被调用时,实际上是在共享对象的引用。这种特性对于不可变对象(如整数)来说,其在函数内部...
默认参数允许我们为函数的参数指定默认值,如果调用时没有提供相应的参数值,则会自动使用这些默认值。这种特性在提高代码复用性、简化函数调用以及编写更灵活的代码方面非常有用。本文将详细介绍如何在Python函数中...
在Python编程中,能够从命令行接收参数是一项非常实用的功能。本文将深入探讨如何使用`sys.argv`来接收命令行参数,并通过一个具体的示例——过滤文本文件中的特定关键字——来展示这一功能的具体应用。 #### 一、...
在调用函数的过程中参数是如何被解析先看第一个问题,在python中函数参数的定义主要有四种方式: 1.F(arg1,arg2,…) 这 是最常见的定义方式,一个函数可以定义任意个参数,每个参数间用逗号分割,用这种方式定义的...
Python函数的默认参数是编程中常见的特性,它允许我们在调用函数时省略某些参数的值,这些参数将使用在函数定义时设定的默认值。然而,如果不正确地使用默认参数,可能会导致一些意外的问题。以下是一些关于Python...
在Python编程语言中,`factor`函数通常是指用于因式分解整数的函数,但这在提供的文本中并未直接介绍。然而,我们可以从描述和部分内容中提取出几个关键知识点: 1. **参数收集与解包**: - Python允许在函数定义...
2. **函数参数**: - 函数可以接受参数,如`isPrime`函数中的`v`,用于传递值到函数内部进行处理。 - 参数类型可以是整数、字符串等,例如`demo`函数接收一个字符串`v`,统计其中的大写字母、小写字母、数字和其他...
Python也可用于可定制化软件中的扩展程序语言。Python丰富的标准库,提供了适用于各个主要系统平台的源码或机器码。 Python还被语言流行指数的编译器Tiobe将它被评为最受欢迎的编程语言,20多年来首次将Python置于...
在探讨Python函数参数传递机制之前,我们首先需要理解Python中变量与对象的关系。不同于C/C++等语言,Python中的变量更像是对象的引用或标签,而不是一个单独的内存空间。因此,理解Python中的参数传递方式,必须先...
本文将通过具体的代码示例和内存地址分析,帮助读者更深入地理解Python函数参数传递的本质。 #### Python中参数传递的基本原理 Python函数参数传递本质上是基于对象的引用。这意味着当我们将一个对象作为参数传递...
4. **准备参数**:根据Python函数的参数类型,我们需要创建相应的Python对象作为参数。例如,如果函数需要一个整数参数,我们可以使用`PyLong_FromLong()`创建一个整数对象。 5. **调用Python函数**:使用`PyObject...
在Python编程语言中,函数是组织代码的重要方式,而函数的默认参数则是提高代码复用性和灵活性的一个关键特性。在《Python应用开发-函数默认参数》的主题中,我们将深入探讨如何使用和理解Python函数的默认参数。 ...
在Python编程语言中,函数是组织良好、可重用的代码块,它们执行特定任务并可能接收输入(参数)和返回结果(返回值)。函数在软件开发中扮演着至关重要的角色,因为它们有助于提高代码的模块化和可读性。 **函数的...
总之,理解Python函数参数的使用,包括必选参数、默认参数、可变参数和关键字参数,以及如何利用它们编写清晰、灵活的代码,对于Python开发者来说至关重要。同时,对递归和尾递归的掌握也能帮助解决一些复杂问题。
本文实例讲述了Python中函数及默认参数的定义与调用操作。分享给大家供大家参考,具体如下: #coding=utf8 ''''' Python中的函数使用小括号调用。函数在调用之前必须先定义。 如果函数中没有return语句,就会自动...
Python函数的默认参数是编程中常见的特性,它允许在不提供所有必需参数的情况下调用函数。这大大简化了代码,特别是在处理重复性任务时。然而,如果不正确地使用默认参数,可能会导致意料之外的结果。下面我们将深入...
#### 四、函数参数 ##### 1、位置参数与关键字参数 - **位置参数**:按照参数的位置顺序进行传递。例如: ```python def test(a, b, c): print(a) print(b) print(c) test(1, 2, 3) # 输出1 2 3 ``` - **...
描述中提到的“5行代码,用python匿名函数实现表达式求值”,指的是利用Python的lambda函数和eval()内置函数来简洁地完成这个功能。 **lambda函数**是Python中的一个简洁方式,用于创建匿名函数,即没有名字的函数...