三、函数参数
1、传递参数
- 参数的传递是通过自动将对象赋值给本地变量名来实现的;作为参数被传递的对象从来不自动拷贝。
- 不可变参数“通过值”进行传递。实际就像创建了一份拷贝。
- 可变对象是通过“指针”进行传递。可变对象能够在函数内部进行原处的改变。
- 在函数内部的参数名的赋值不会影响调用者:在函数运行时,在函数头部的参数名是一个新的、本地的变量名,这个变量名是在函数的本地作用域内的。
- 改变函数的可变对象参数的值也许会对调用者有影响。参数是简单地赋值给传入的对象,函数能够给就地改变传入的可变对象。
2、参数和共享引用
参数名称可能最初共享传递的 对象(它们实质上是指向这些对象的指针),但只是临时的,即当函数第一次调用时只要对参数名进行重新赋值,这种关系就结束了。
- 以上现象说明:对可变对象的修改会影响其他引用了该对象的变量。这种行为不是一个bug----它只是参数传递在Python中工作的方式:Python中默认通过引用(指针)进行函数的参数传递;可通过对传入对象的拷贝避免:
2、特定的参数匹配模型
默认,参数是通过其位置进行匹配的,从左至右,且必须精确地传递和函数头部参数名一样多的参数
(1)位置参数:从左至右匹配
(2)关键字参数(name=value):通过参数名匹配
- 在调用中起数据标签的作用;与使用的默认参数进行配对。
(3)默认参数:为没有传入值的参数定义参数值;只会执行一次!
- 要设置动态默认值,习惯上是把默认值设为None。一个函数定义中,在第一个拥有默认值的参数之后的任何参数都必须拥有默认值。
- 默认参数必须指向不变对象!一个坑
原因是,Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。
修改后
- 设计不变对象的原因:因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了优于修改数据导致的错误,此外由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。所以在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。
(4)可变参数(*args):收集任意多个基于位置或关键字的参数
- 传入的参数个数是可变的,*args,args接收的是一个tuple。
- 在参数前面加一个*号,可放在其他位置(不是最后),且不会收集关键字参数;
- Python允许在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去。
(5)关键字参数:**kw
- 允许传入0个或任意个参数名的参数,这些关键字参数在函数内部自动组装为一个dict。
(6)可变参数解包:传递任意多各基于位置或关键字的参数
- 调用者能够再使用* 语法将参数集合打散,分成参数:这个“*”与函数头部的“*”相反:在函数头部它以为着收集任意数量参数;在调用者中意味着传递任意数量参数(解包任意数量的参数)。解包参数的集合,而不是创建参数的集合。
(7)命名关键字参数:参数必须按照名称传递
- 函数指定参数,参数必须用带有关键参数的名称(而不是位置)传递。
- 有 默认值的参数是可选的;
- 没有默认值的参数是必须的关键字参数;
- 如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了
- 混合参数匹配
-
- 在函数调用中,参数必须以此顺序出现:任何位置参数(value),后跟任何关键字参数(name=value)和*sequence形式的组合,后跟**dict形式。在函数头部,参数必须以此顺序出现:任何一般参数(name),后跟任何默认参数(name=value),如果有,后跟*name(或*)的形式,后跟任何name或name=vlaue命名关键字参数,后跟**name形式。
- Python内部的参数匹配步骤
- 通过位置分配非关键字参数。
- 通过匹配变量名分配关键字参数。
- 其他额外的非关键字参数分配到*name元组中。
- 其他额外的关键参数分配到**name字典中。
- 用默认值分配给头部未得到分配的参数。