参数传递
参数传递关键点:
- 参数的传递是通过自动将对象赋值给局部变量来实现的,函数参数在实际中只是Python赋值的另一个例子,由于在Python中,引用以指针的形式实现,所以参数实际上都是通过指针传入的,并且作为参数被传递的对象不会自动复制
- 在函数内部对参数名进行赋值不会影响调用者,在函数运行时,函数头部的参数名是一个新的、局部的变量名,这个变量名作用于当前函数的内部,函数参数名和调用者作用域中的变量名没有关联
- 改变函数的可变对象参数的值可能会对调用者有影响,由于直接把传入的对象赋值给参数,函数能够在原位置对传入的可变对象做出更改,因此结果可能会影响调用者
可变于不可变对象的传递:
- 不可变参数本质上传入了“值”,像整数和字符串等对象通过对象引用而非复制传入,但由于无法在原位置修改不可变对象,最终的效果就像是创建了一份副本
# 不可变对象参数传递
def fn1(x):
# 对参数做出修改,将其修改为字符串类型
x = 'a'
print(x)
x = 10
fn1(x)
print(x)
# a
# 10
# 由此可见函数外部定义的变量x的值并未改变
- 可变对象本质上传入了“指针”,列表和字典等对象同样通过对象引用传入,可变对象能够在函数内部做原位置的改变
# 可变对象参数传递
def fn(lst1, lst2):
# 对列表lst1做出修改
lst1.append('string')
# 对lst2重新赋值,并且与原来的值相同
lst2 = [1, 2, 3, 4]
# 对新列表作出修改
lst2.append('<_<')
lst1 = [1, 2, 3, 4]
lst2 = [1, 2, 3, 4]
fn(lst1, lst2)
print(lst1)
print(lst2)
# [1, 2, 3, 4, 'string']
# [1, 2, 3, 4]
# 由此可见,由于函数中传入的是lst1的引用,所以能够在函数内部对lst1进行原位置的修改
# 在函数中同样也传入了lst2的引用,但是lst2作为函数内部的局部变量被重新赋值,lst2指向了新的地址,与原位置的列表无关
参数与共享引用:
- 由上面的示例可见,对函数内的变量lst2赋值不会影响到主调函数作用域中的变量lst2,函数中的lst2被重置为一个完全不同的对象
- 所以变量名没有别名的关联的含义,参数名称初始可能共享传递对象(本质上是指向这些对象的指针),但只是临时的,这种共享关系仅存在于函数刚开始被调用的时候,只要对函数中的参数名重新赋值,这种共享关系就会结束
参数匹配
匹配模式:
- 位置参数,从左至右进行匹配
- 关键字参数,通过参数名进行匹配,使用name=value的形式来指定函数中的哪个参数接收某个值
- 默认值参数,为没有传入值的可选参数指定参数值
- 可变长位置参数, 使用*开头的特殊参数表示收集任意多 (包括0个) 的基于位置的参数, 并将传入的参数收集为元组的形式
- 可变长关键字参数,使用**开头的特殊参数表示收集任意多(包括0个)的基于关键字的参数, 并将传入的参数收集为字典的形式
- 可变长参数解构,在调用中使用*iterable或**dict表示在一个序列(以及其他迭代器)或字典中对应封装任意数量的基于位置或关键字,并且在把它们传给函数的时候,将它们解构为独立的参数
- keyword-only参数,仅Python 3有此模式,所有在name或之后出现的普通参数或带有默认值的参数都将变为 keyword-only参数, keyword-only参数必须在调用时通过关键字传入
匹配模式规则
函数调用及定义的参数顺序:
- 在函数调用中的参数顺序:所有基于位置的参数(value),关键字参数(name=value)和*iterable形式的组合,**dict形式
- 在函数定义中的参数顺序:所有一般参数(name),所有默认值参数(name=value),name(或Python 3中的)形式,所有name或name=value的keyword-only参数,**name形式
Python内部匹配参数的步骤:
- 通过位置分配无关键字的参数
- 通过匹配名称分配关键字参数
- 将剩下的非关键字参数分配到*name元组中
- 将剩下的关键字参数分配到**name字典中
- 将默认值分配给在定义时未得到匹配的参数
位置参数、关键字参数及默认值参数的测试:
# 位置参数、关键字参数及默认值参数的测试
def fn(a, b, c, d=10, e='^_^'):
print("a:%s, b:%s, c:%s, d:%s, e:%s" % (a, b, c, d, e))
a = 1
b = 2
c = 3
d = '?????'
e = 99
f = 'not in'
# z正确调用形式
fn(a, b, f, d)
# a:1, b:2, c:not in, d:?????, e:^_^
fn(a, c = '11', b = '99')
# a:1, b:99, c:11, d:10, e:^_^
# 关键字参数在传入时无关顺序
# 错误调用形式
fn(a, b)
# TypeError: fn() missing 1 required positional argument: 'c'
# 确少参数
fn(a, b, c, d, e, f)
# TypeError: fn() takes from 3 to 5 positional arguments but 6 were given
# 过多的参数
fn(a, b, f=100)
# TypeError: fn() got an unexpected keyword argument 'f'
fn(a = 4, b, c)
# SyntaxError: positional argument follows keyword argument
# 关键字参数不能出现在位置参数之前
可变长参数测试:
- **在函数定义时,*或*后面的变量名除有明确的意义外,一般情况下都定义为*args或*kwargs
# 可变位置参数的测试
def fn1(x, y=5, *args):
# 测试args的类型及其内容
print(type(args), args)
sum = x + y
for x in args:
sum = sum + x
return sum
print(fn1(1, 2, 3, 4, 5, 6, 7))
# <class 'tuple'> (3, 4, 5, 6, 7)
# 28
print(fn1(1))
# <class 'tuple'> ()
# 6
# 错误调用形式
print(fn1(1, 2, 3, 4, x = 5))
# TypeError: fn1() got multiple values for argument 'x'
# x接收了两个以上的参数
# 可变关键字参数的测试
def fn2(x, y = 2, *args, **kwargs):
print(x, y)
print(args)
print(type(kwargs), kwargs)
fn2(1, 2, 3, 4, 5, hsotname = '172.21.18.123', username = 'corazon', passwd = 'donquixote' )
# 1 2
# (3, 4, 5)
# <class 'dict'> {'hsotname': '172.21.18.123', 'username': 'corazon', 'passwd': 'donquixote'}
可变长参数解构测试:
def fn3(a, b):
print('a=%s, b=%s'%(a, b))
# 对接收的可迭代对象解构
fn3(*[1, 2])
# a=1, b=2
fn3(*{3, 4})
# a=3, b=4
fn3(*range(2))
# a=0, b=1
fn3([5, 6, 7])
# TypeError: fn3() missing 1 required positional argument: 'b'
# 对接收的字典进行解构
fn3(**{'a':5, 'b':6})
# a=5, b=6
fn3(**{'x':7, 'y':8})
# TypeError: fn3() got an unexpected keyword argument 'x'
fn3(*{'x':7, 'y':8})
# a=x, b=y
fn3(*{'x':7, 'y':8}.keys())
fn3(*{'x':7, 'y':8}.values())
fn3(*{'x':7, 'y':8}.items())
# a=x, b=y
# a=7, b=8
# a=('x', 7), b=('y', 8)
keyword-noly参数测试:
- 出现在参数列表*args之后的又名参数称为keyword-noly参数,所有这些参数都必须在调用中使用关键字语法来传递
- 在参数列表中可以仅使用一个来表示 函数不会接收一个可变长度的参数列表,但是期待所有跟在后面的参数以关键字参数的形式传入
def fn4(a, *args, c ,d=99, **kwargs):
print('a=%s, args=%s'%(a, args))
print('c=%s, d=%s'%(c, d))
print('kwargs=%s'%kwargs)
fn4(1, 2 ,3 ,4 , 5, c=6)
# a=1, args=(2, 3, 4, 5)
# c=6, d=99
# kwargs={}
fn4(1, 2, 3, 4, 5)
# TypeError: fn4() missing 1 required keyword-only argument: 'c'
fn4(1, 2, 3, 4, **{'c':99, 'd':100, 'e':'zoro'})
# a=1, args=(2, 3, 4)
# c=99, d=100
# kwargs={'e': 'zoro'}
def fn5(a, *, b, c):
print('a=%s'%a)
print('b=%s, c=%s'%(b, c))
# fn5(1, 2, 3, 4, 5)
# TypeError: fn5() takes 1 positional argument but 5 were given
# *不接受参数
fn5(1, b=2, c=3)
# a=1
# b=2, c=3