3.2 参数传递
1 基本传参
把数据用参数形式输入到函数, 被称为参数传递
若有多个参数, 那么在调用函数时
py 会根据位置来确认数据对应哪个参数, 例如:
- def print_arguments(a,b,c)
- print(a,b,c)
print_arguments(1,3,5) #打印 1,3,5
print_arguments(5,3,1) #打印 5,3,1
print_arguments(3,5,1) #打印 3,5,1
在程序的三次调用中, py 都是通过位置来确定实参与形参的对应关系的
如果觉得位置传参比较死板, 那么可以用关键字 (Keyword) 的方式来传递参数
在定义函数时, 我们给了形参一个符号标记, 即参数名
关键字传递是根据参数名来让数据与符号对应上
因此, 如果在调用时使用关键字传递, 那么不用遵守位置的对应关系
沿用上面的函数定义, 改用参数传递的方式:
print_arguments(c=5,b=3,a=1) #打印 1,3,5
位置传递与关键字传递可以混合使用, 即一部分的参数传递根据位置, 另一部分根据参数名
在调用函数时, 所有的位置参数都要出现在关键字参数之前
因此, 你可以用如下方式来调用:
print_arguments(1,c=5,b=3) #打印 1,3,5
print_arguments(c=5,1,b=3) #程序报错
位置传递和关键字传递让数据与形参对应起来
因此数据个数与形参的个数应该相同
但在函数定义时, 我们可以设置某些形参的默认值
如果我们在调用时不提供这些形参的具体数据
那么它们将采用定义时的默认值, 比如:
- def f(a,b,c=10):
- return a+b+c
print(f(3,2,1)) #参数 c 取传入的 1, 结果打印 6
print(f(3,2)) #参数 c 取默认值 10, 结果打印 15
第一次调用函数时输入了 3 个数据, 正好对应三个形参
因此形参 c 对应的数据是 1, 第二次调用函数时
提供了 3 和 2 两个数据, 函数根据具体位置, 把 3 和 2 对应成形参 a 和 b
到了形参 c 时, 已经没有多余的数据, 所以 c 将采用默认值 10
2. 包裹传参
以上传递参数的方式, 都要求在定义函数时说明参数的个数.
但有时在定义函数时, 并不知道参数的个数, 其原因有很多
有时是确实不知道参数的个数, 需要在程序运行时才知道
有时是希望函数定义更加松散, 以便于函数能运用于不同形式的调用
这时候, 用包裹 (packing) 传参的方式来进行参数传递会非常有用
包裹传参也有位置和关键字两种形式
下面是包裹传参的例子:
- def package_position(*all_arguments):
- print(type(all_arguments))
- print(all_arguments)
- package_position(1,4,6)
- package_position(5,6,7,1,2,3)
- <class 'tuple'>
- (1, 4, 6)
- <class 'tuple'>
- (5, 6, 7, 1, 2, 3)
两次调用, 尽管参数个数不同, 但是都基于同一个 package_position()定义
在调用 package_position()时, 所有数据都根据先后顺序, 收集到一个元组
在函数内部, 可以通过元组来读取传入数据, 这就是包裹位置传参
为了提醒 py 参数 all_arguments 是包裹位置传递所用的元组名
我们在定义 package_position()时要在元组名 all_arguments 前加上 * 号
再看包裹关键字传递的例子, 这一参数传递方法把传入的数据收集为一个词典:
- def package_keyword(**all_arguments):
- print(type(all_arguments))
- print(all_arguments)
- package_keyword(a=1,b=9)
- package_keyword(m=2,n=1,c=11)
- <class 'dict'>
- {'a': 1, 'b': 9}
- <class 'dict'>
- {'m': 2, 'n': 1, 'c': 11}
与上面一个例子相似, 当函数调用时, 所有参数会收集到一个数据容器里
只不过, 在包裹关键字传递时, 数据容器不再是一个元组
而是一个字典, 每个关键字形式的参数调用, 都会成为字典的一个元素.
参数名成为元素的键, 而数据成为元素的值
字典 all_arguments 收集了所有的参数, 把数据传递给函数使用
为了提醒, 参数 all_arguments 是包裹关键字传递所用的字典
因此在 all_arguments 前加 **
包裹位置传参还能与包裹关键字传参混合使用:
- def package_mix(*positions,**keywords):
- print(positions)
- print(keywords)
- package_mix(1,2,3,a=7,b=8,c=9)
- (1, 2, 3)
- {'a': 7, 'b': 8, 'c': 9}
还可以更进一步, 把包裹传参和基本传参混合使用
它们出现的先后顺序是: 位置 ->关键字 ->包裹位置 ->包裹关键字
有了包裹传递, 我们在定义函数时可以更灵活地表示数据
3. 解包裹
除了用于函数定义,* 和 ** 还可以用于函数调用
这时候, 两者是为了实现一种叫做解包裹 (Unpacking) 的语法
解包裹允许我们把一个数据容器传递给函数, 再自动的分解为各个参数
需注意的是, 包裹传参和解包裹并不是反向操作, 而是两个相对独立的功能
下面是解包裹的一个例子:
- def unpackage(a,b,c):
- print(a,b,c)
- args = (1,3,4)
unpackage(*args) #结果为 1 3 4
在这个例子中, unpackage()使用了基本传参的方法
函数有三个参数, 按照位置传递, 但是在调用该函数时
我们用了解包裹的方式, 可以看到, 我们调用函数时传递的是一个元组
按照基本传参的方式, 一个元组是无法和三个参数对应上的
但是我们通过在 args 前面加上 * 符号, 来提醒 py, 我想把元组拆分成三个元素
每个元素对应函数的一个位置参数, 于是, 元组的三个元素分别被赋予了三个参数
相应的, 词典也可用于解包裹, 使用相同的 unpackage()定义:
args = {'a':1,'b':2,'c':3}
unpackage(**args) #打印 1,2,3
解包裹用于函数调用, 在调用时, 几种参数传递方式也可以混合
依然是相同的基本原则: 位置 ->关键字 ->包裹位置 ->包裹关键字 8
来源: http://www.jianshu.com/p/6a8ca0690410