传递参数
函数传递参数时的一些简要的关键点:
- 参数的传递是通过自动将对象赋值给本地变量名来实现的。所有的参数实际上都是通过指针进行传递的,作为参数被传递的对象从来不自动拷贝。
- 在函数内部的参数名的赋值不会影响调用者。
- 改变函数的可变对象参数的值会对调用者有影响。
实际上,Python的参数传递模型和C语言的相当相似:
不可变参数”通过值”进行传递。像整数和字符串这样的对象是通过对象引用而不是拷贝进行的,但是因为不论怎么样都不可能在原处改变不可变对象,实际的效果就很像创建了一份拷贝。
可变对象是通过”指针”进行传递的。这就意味着,可变对象能够在函数内部进行原处修改。
>>避免可变参数的修改
避免参数的修改有很多种方式:
传递参数时,传递一个拷贝:
L = [1,2] changer(L[:])</div>
函数内部进行拷贝
def changer(b): b=b[:]</div>
将可变对象转化为不可变对象
L=[1,2] changer(tuple(L))</div>
>>对参数输出进行模拟
对于参数的返回值有一个小技巧:因为return能够返回任意种类的对象,如果这些值封装进一个元组或其他的集合类型,那么它也能够返回多个值。
def multiple(x,y): x = 2 y = [2,4] return x,y #Return new values in a tuple</div>
这段代码貌似返回了两个值,其实只有一个:一个包含了2个元素的元组,它的括号是可以省略的。
特定的参数匹配模型
>>基础知识
匹配模型的大纲:
- 位置:从左至右进行匹配。
- 关键字参数:通过参数名进行匹配。(调用者可以定义哪一个函数接受这个值,通过在调用时使用参数的变量名,使用name=value这种语法。)
- 默认参数:为没有传入值的参数定义参数值。
- 可变参数:搜集任意多基于位置或关键字的参数。
- 可变参数解包:传递任意多的基于位置或关键字的参数。
- Keyword-only参数:参数必须按照名称传递。(只存在于Python3.0中)
>>匹配语法
| 语法 | 位置 | 解释 |
|---|---|---|
| func(value) | 调用者 | 常规参数:通过位置进行匹配。 |
| func(name=value) | 调用者 | 关键字参数:通过变量名匹配。 |
| func(*sequence) | 调用者 | 以name传递所有的对象,并作为独立的基于位置的参数。 |
| func(**dict) | 调用者 | 以name成对的传递所有的关键字/值,并作为独立的关键字参数。 |
| def func(name) | 函数 | 常规参数:通过位置或变量名进行匹配。 |
| def func(name=value) | 函数 | 默认参数值,如果在调用中传递的话。 |
| def func(*name) | 函数 | 匹配并收集(在元组中)所有包含位置的参数。 |
| def func(**name) | 函数 | 匹配并收集(在字典中)所有包含位置的参数。 |
| def func(*args,name) | 函数 | 参数必须在调用中按照关键字传递。 |
| def func(*,name=value) | 函数 | 参数必须在调用中按照关键字传递。(Python3.0) |
相应的说明:
在函数的调用中(表中的前4行),简单的通过变量名位置进行匹配,但是使用name=value的形式告诉Python依照变量名进行匹配,这些叫做关键字参数。在调用中使用*sequence或**dict允许我们在一个序列或字典中相应地封装任意多的位置相关或者关键字的对象,并且在将他们传递给函数的时候,将它们解包为分开的、单个的参数。
在函数的头部,一个简单的变量名时通过位置或变量名进行匹配的(取决于调用者是如何传递给它参数的),但是name=value的形式定义了默认的参数值。*name的形式收集了任意的额外不匹配的参数到元组中,并且**name的形式将会手机额外的关键字参数到字典中。在Python3.0及其以后的版本中,跟在*name或一个单独的*之后的、任何正式的或默认的参数名称,都是keyword-only参数,并且必须在调用时按照关键字传递。
>>细节
在使用混合的参数模型的时候,Python将会遵循下面有关顺序的法则。
在函数调用中,参数必须以此顺序出现:任何位置参数(value),后面跟着任何关键字参数(name=value)和*sequence形式的组合,后面跟着**dict形式。
在函数头部,参数必须以此顺序出现:任何一般参数(name),紧跟着任何默认参数(name=value),后面是name(在Python3.0中是)形式,后面跟着任何name或name=value keyword-only参数(Python3.0中),后面跟着**name形式。
在调用和函数头部中,如果出现**arg形式的话,都必须出现在最后。
Python内部是使用以下的步骤来在赋值前进行参数匹配的:
- 通过位置分配非关键字参数。
- 通过匹配变量名分配关键字参数。
- 其他额外的非关键字分配到*name元组中。
- 其他额外的关键字参数分配到**name字典中。
- 用默认值分配给在头部未得到分配的参数。
- 在这之后,Python会进行检测,确保每个参数只传入了一个值。如果不是这样的话,将会发生错误。当所有匹配都完成了,Python把传递给参数名的对象赋值给它们。
>>关键字参数和默认参数的实例
如果没有使用任何特殊的匹配语法,Python默认会通过位置从左至右匹配变量名。
def f(a,b,c): print(a,b,c) f(1,2,3) #Prints 1,2,3</div>
关键字参数
关键字参数允许通过变量名进行匹配,而不是通过位置。
f(c=3,b=2,a=1) #Prints 1,2,3</div>
默认参数
默认参数允许创建函数可选的参数。如果没有传入值的话,在函数运行前,参数就被赋了默认值。
def f(a,b=2,c=3): print(a,b,c) f(1) #Prints 1,2,3 f(1,4) #Prints 1,4,3 f(1,c=6) #Prints 1,2,6</div>
关键字参数和默认参数的混合
def func(spam,eggs,totast=0,ham=0): print((spam,eggs,totast=0,ham=0)) func(1,2) #Ouput:(1,2,0,0) func(1,ham=1,eggs=0) #Ouput:(1,0,0,1) func(spam=1,eggs=0) #Ouput:(1,0,0,0) func(toast=1,eggs=2,spam=3) #Ouput:(3,2,1,0) func(1,2,3,4) #Ouput:(1,2,3,4)</div>
>>任意参数的实例
最后两种匹配扩展,*和**,是让函数支持接收任意数目的参数的。
收集参数
在函数定义中,在元组中收集不匹配的位置参数。
def f(*args):print(args)</div>
当这个函数调用时,Python将所有位置相关的参数收集到一个新的元组中,并将这个元组赋值给变量args。因此它是一个一般的元组对象,能够进行索引或迭代。
**特性类似,但是它只对关键字参数有效。将这些关键字参数传递给一个新的字典,这个字典之后将能够通过一般的字典工具进行处理。在这种情况下,**允许将关键字参数转化为字典,你能够在之后使用键调用进行步进或字典迭代。
def f(a,*pargs,**kargs):print(a,pargs,kargs) f(1,2,3,x=1,y=2)

