【Python学习】python学习手册--第十八章 参数

传递参数

  • 参数的传递是通过将对象赋值给函数本地变量名来实现的。传递就是赋值,函数本地变量名是对传递进来的对象的引用。所以默认情况下,被传递的对象从来不会自动拷贝
  • 在函数内部的参数名的赋值不会影响调用者。函数头部的参数,是函数本地作用域内的变量名,与调用者作用域内的变量名不会重叠。
  • 改变函数的可变对象参数的值也许会对调用者有影响。
  • 不可变参数“通过值”进行传递。像整数和字符串这类对象是通过对象引用而不是拷贝来进行传递的,当然,这类对象都是不可变的类型,在函数的作用域内的效果就像是创建了一份拷贝。
  • 可变对象是通过“指针”进行传递。可变对象通过引用传递给函数参数,意味着在函数内部对可变对象的修改就可能影响到该对象在调用者中的使用。
>>> def change(x):
...     x=33                                 
...     print(x," in the change function")
... 
>>> x=22
>>> change(x)               #在函数内部的赋值操作不会改变调用者中的变量
33  in the change function
>>> x
22
>>> 
>>> def change(x):
...     x.append(33)
...     print(x," in the change function")
... 
>>> x=[1,2,3,4,5]
>>> change(x)
[1, 2, 3, 4, 5, 33]  in the change function
>>> x                   #可变对象能在原处修改,在函数内部的修改相对于在对象本身做修改
[1, 2, 3, 4, 5, 33]
>>> 

这里如果理解了变量名与对象之间的关系,你就能更清楚的理解参数的传递。可以查看之前的博文

避免改变可变参数

为了避免可变参数在函数内部被修改,你可以传递对象的新拷贝进入函数,而不是默认赋值情况下,传递对象的引用进入函数。

>>> def change(x):
...     x.append(33)
...     print(x," in the change function")
... 
>>> x=[1,2,3,4,5]
>>> x
[1, 2, 3, 4, 5]
>>> change(x[:])        #传递列表的拷贝,如果是其它可变类型,可以使用copy.copy或copy.deepcopy函数
[1, 2, 3, 4, 5, 33]  in the change function
>>> x
[1, 2, 3, 4, 5]
>>> 

参数匹配模型

传递的参数可以是多个,在有多个参数传递的时候,就存在参数匹配函数本地变量的模型,模型是根据函数的变量名来选择的,选择完成之后,对函数本地变量名的传递本质还是赋值。
参数匹配模型有以下几种方式:

  • 位置:从左至右进行匹配
  • 关键字参数:通过参数名进行匹配,传递变量时用name=value
  • 默认参数:在没有参数传入时定义的默认值,形式通常在定义函数时使用name=value
  • 可变参数:收集任意多基于位置或关键字的参数(形式就像*arg用于收集列表 或**arg用于收集字典),一般在函数的定义时出现。
  • 可变参数解包:传递任意多的基于位置或关键字的参数(形式就像*arg用于传递列表 或**arg用于传递字典)。在函数的调用时出现。
  • keyword-only参数:必须按照关键字传递的参数

在函数定义时,参数匹配模型的语法:

语法 解释
def func(name1,name2,name3) 最常见的匹配模式,通过从左至右的位置顺序进行匹配
def func(name1=value1,…name3=value3) 默认参数值,如果没有在调用中传递值的话
def func(*name) 匹配并收集元组中所有包含位置的参数
def func(**name) 匹配并收集字典中所有包含关键字的参数
def func(*args,name) 参数必须在调用中按照关键字传递
def func(*, name=value) Python3.0中引进,任何默认的或正式的参数都是keyword-only参数,必须在调用时按照关键字传递。

在函数调用时,也有相关的参数匹配模式:

语法 解释
func(value1,value2,value3) 最常见的匹配模式,按照从左到右的位置将参数值匹配进函数本地变量
func(name1=value1,name2=value2,name3=value3) 通过关键字匹配,表现形式更直观,将函数的参数赋值
func(*sequence) 以sequence列表传递基于从左至右位置顺序的参数。
func(**dict) 字典中的键为函数的参数变量名,在字典中传递相应的值给函数变量名

在函数定义的头部,

  • 一个简单的参数名是通过位置或变量名进行匹配的(可以用位置传递,也可以用关键字传递)
  • 头部出现name=value,指定了参数名的默认值
  • *name收集了任意的额外不匹配的参数到元组中
  • **name的形式将会收集额外的关键字参数到字典中

其中关键字参数和默认参数在Python中是比较常见的使用方式。
如果要决定并混合特定的参数匹配模型,Python将会遵循下面有关顺序的法则:

  • 在调用函数时,参数必须以此先后顺序出现:任何位置参数(value),任何关键字参数(name=value)和*sequence形式的组合,最后是**dict形式
  • 在函数头部,参数必须以此顺序出现:任何一般参数(name),默认参数(name=value),*name的形式,跟一般参数或关键字参数,最后跟**name形式的参数

不管是调用或者定义函数的时候,**arg形式就必须出现在最后。
在Python内部是使用以下的步骤来在赋值前进行参数匹配的:

  • 通过位置分配非关键字参数
  • 通过匹配变量名分配关键字参数
  • 其它额外的非关键字参数分配到*name元组中
  • 其它额外的关键字参数分配到**name字典中
  • 用默认值分配给函数头部没有获得值的变量

参数调用实例

>>> def func(a,b,c): print(a,b,c)
... 
>>> func(1,2,3)               #按照位置顺序,依次赋值参数名
1 2 3
>>> func(a=3,c=2,b=1)         #也可以通过关键字的形式,给函数参数赋值
3 1 2
>>> func(1,b=2,c=3)          #混用的情况
1 2 3
>>> func(a=1,2,3)            #匹配模式混用时要注意按照上文所说的出现顺序来使用。
  File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument
>>> 

任意参数的实例

可以用*arg的形式,接受传递进来的任意值,并把它们组成元组:

>>> def func(*arg):print(arg)        
... 
>>> func()
()
>>> func(1)
(1,)                #得到的是元组对象
>>> func(1,2,3,4,5)
(1, 2, 3, 4, 5)
>>> 

而**arg也有相同的效果,但是他们得到的是字典对象:

>>> def func(**arg):print(arg)
... 
>>> func()
{}
>>> func(a=1)
{'a': 1}
>>> func(a=1,b=2,c=3,d=4)          #传入的形式是关键字的形式传递进入函数中,函数中得到的是字典的形式
{'a': 1, 'b': 2, 'd': 4, 'c': 3}
>>> 

当这两种形式混用时,可以给函数带来极大的灵活性:

>>> def func(a,b,*c,**d):
...     print('a,b:',a,b)
...     print('c:',c)
...     print('d:',d)
... 
>>> func(1,2)
a,b: 1 2
c: ()
d: {}
>>> func(1,2,3,4,5,6,7,z=1,x=3,y=4)
a,b: 1 2
c: (3, 4, 5, 6, 7)
d: {'z': 1, 'x': 3, 'y': 4}
>>> 

解包参数

相反的,当你在调用函数时,使用*arg或**arg的语法,可以达到解包参数的效果(函数定义时使用这两种形式,就像是把多余的参数打包成为列表或字典的形式,传递进入函数中)。
调用时使用*arg,就是把列表中的元素,分解为一个个值,传递给函数参数,同理,使用**arg时,会以键/值的形式解包一个字典,并将其以关键字的形式传递给函数参数:

>>> def func(a,b,c,d):
...     print('a,b,c,d:',a,b,c,d)
... 
>>> l=[1,2,4,3]
>>> func(*l)
a,b,c,d: 1 2 4 3
>>> l={'d':99,'a':33,'b':22,'c':11}
>>> func(**l)
a,b,c,d: 33 22 11 99

keyword-only参数

在函数定义时,在*arg或*之后的参数,都必须使用关键字的形式传递参数值。在没有默认值的情况下,这些参数都必须通过关键字的形式赋值:


>>> def func(a,b,*,c,d):       #c,d必须使用关键字的形式来赋值
...     print('a,b,c,d:',a,b,c,d)
... 
>>> func(1,2,c=4,d=6)
a,b,c,d: 1 2 4 6
>>> func(1,2,4,6)         #不使用关键字就会报错,从语法上来讲,4,6这两个数已经被打包至“*”列表中。
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() takes 2 positional arguments but 4 were given
>>> 

需要注意的是,**arg这样的形式,只能出现在函数参数列表的最后面。不满足这样的要求都会报错。

小结

本章中的内容介绍了函数中的参数,了解到Python是通过传递引用给参数的。在参数传递的过程中有很多种参数匹配的模式,这些模式使得Python程序由很好的灵活性。还有更高级的扩展:包括默认参数、关键字参数、多个任意参数的工具。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章