第十八章 参数

参数传递基础

下面是给函数传递参数时的一些简要的关键点:

  • 参数的传递是通过自动将对象赋值给局部变量名来实现的;
  • 在函数内部赋值参数名不会影响调用者;
  • 改变函数的可变对象参数的值也许会对调用者有影响;
  • 不可变参数本质上传入了‘值’;
  • 可变对象本质上传入‘指针’;

参数和共享引用

>>> def f(a):
...     a = 99
... 
>>> b = 88
>>> f(b)
>>> print(b)
88

避免修改可变参数

这里最需要记住的就是,函数能够改变传入的可变对象(例如,列表和字典),这不会是个麻烦,井且有时候这对于某些用途很有用处。此外,原位置修改传入的可变对象的函数,可能是有意面为的,例如,修改可能是一个定义良好的AP的一部分,而我们不应该通过创建复制来违反该API。
然而,你也必须意识到这个特性:如果在你没有预期的情况下外部对象发生了改变,检查一下是不是由某个被调用的函数引起的,并且有必要的话在传入对象时创建复制。

模拟输出参数和多重结果

>>> def multiple(x, y):
...     x = 2
...     y = [3, 4]
...     return x, y
... 
>>> 
>>> X = 1
>>> L = [1, 2]
>>> X, L = multiple(X, L)
>>> X, L
(2, [3, 4])

看起来这段代码好像返国了两个值,但是实际上只有一个:一个包含2个元素的元组这里省略了它可选的圆括号,在调用返国后,我们能使用元组赋值解包被返国元组的各部分。

特殊的参数匹配模型

参数匹配基础

匹配模式的大纲:

  • 位置次序:从左至右进行匹配
    这是一般情况,也是我们目前为止最常用的方法,即通过位置进行匹配把参数值传递给函数头部的参数名称,匹配顺序为从左到右;
  • 关键字参数:通过参数名进行匹配
    此外,调用者可以通过在调用时使用参数的变量名,即使用name = value这种语法,来指定函数中的哪个参数接受某个值;
  • 默认值参数:为没有传入值的可选参数指定参数值
    如果调用时的传入值过少的话,函数自身能够为参数指定接受的默认值,这将同样用到语法name= value;
  • 可变长参数( varanus)收集:收集任意多的基于位置成关键字的参数
    函数能够用一个星号“”或两个星号“**”开头的特殊参数,来收集任意多的额外参数。这个特性常常叫作可变长参数,借鉴自C语言中的可变长度参数列表工具,在Pyhon中,参数被收集到一个普通的对象中;
  • 可变长参数解包:传人任意多的基于位置或关键字的参数
    调用者也能使用语法去将参数集合解包成单个的参数。这个“”与在函数头部的“恰恰相反:在函数头部的“*”意味着收集任意多的参数,而在调用者中意味着解包任意多的参数并将它们作为离散的值单独传人;
  • keyword-only参数:必须按照名称传递的参数
    在 Python3.X中(不包括 Python2.X),函数也能用关键字参数来指定必须通过名称(而不是位置)来传递的参数。这样的参数通常用来定义实际使用的参数以外的配置选项;
语法位置解释
func(value)调用者常规参数:通过位置匹配
func(name=value)调用者关键字参数:通过名称匹配
func(*iterable)调用者将 iterable中所有的对象作为独立的基于位置的参数传入
func(**dict)调用者将dct中所有的键/值对作为独立的关键字参数传入
def func(name)函数常规参数:通过位置或名称匹配
def func( name=value)函数默认值参数值,如果没有在调用时传入的话
def func(*name)函数数将剩下的基于位置的参数匹配井收集到一个元组中
def func(**name)函数将剩下的关键字参数匹配并收集到一个字典中
def func(* other,name)函数在调用中必须通过关键字传入的参数(仅限3X)
def func(*, name=value)函数在调用中必须通过关键字传入的参数(仅限3X)

更深入的细节

函数参数应遵循下面这些顺序规则:

  • 在函数调用时,参数必须按此顺序出现:
    所有基于位置的参数(value);
    所有关键字参数(name=value);
    *iterable形式的组合;
    **dict;
  • 在函数头部,函数必须按此顺序出现:
    所有一把参数(name);
    所有默认值参数(name=value);
    *name形式;
    name=value的keyword-only参数,之后是**name形式;

python内部大致是使用以下的步骤来在赋值前匹配参数的:

  1. 通过位置分配无关键字参数;
  2. 通过匹配名称分配关键字参数;
  3. 将剩下的关键字参数分配到**name元组中;
  4. 把默认值分配给在头部未得到匹配的参数;

关键字参数和默认值参数的示例

>>> def f(a, b, c): print(a, b, c)
... 
>>> f(1, 2, 3)
1 2 3

这里,我们基于位置传入值:a匹配到1,b匹配到2,c匹配到3;

关键字参数

关键字参数允许我们通过名称匹配,而不是基于位置。还是同一个函数

>>> f(c=3, b=2, a=1)
1 2 3

默认值参数

简而言之,默认值参数允许我们让特定的参数变为可选的。如果没有传入值的话,在函数运行前参数就会被赋予默认值。例如,下面是这个函数需要一个参数,另外两个参数默认:

>>> def f(a, b=2, c=3): print(a, b, c)
... 
>>> f(1)
1 2 3
>>> f(1, 4)
1 4 3
>>> f(1, c=6)
1 2 6

混合使用关键字参数和默认值参数

下面这段代码中,调用者必须总是传入至少两个参数(匹配spam和eggs),不过另外两个是可选的:

def func(spam, eggs, toast=0, ham=0):
	print(spam, eggs, toast, ham)

func(1, 2)						# output:(1, 2, 0, 0)
func(1, ham=1, eggs=0)			# output:(1, 0, 0, 1)
func(spam=1, eggs=0)			# output:(1, 0, 0, 0)
func(toast=1, eggs=2, spam=3)	# output:(3, 2, 1, 0)
func(1, 2, 3, 4)				# output:(1, 2, 3, 4)

可变长参数的实例

‘*’和‘**’作为最后两种匹配的扩展,旨在让函数支持接受任意多的参数。

函数定义中:收集参数

其中第一种用法,是在函数定义中把不能匹配的基于位置的参数收集到一个元组:

>>> def f(*args):
...     print(args)
... 
>>> f(1, 2, 3, 4)
(1, 2, 3, 4)

‘**’的特性与之类似,但是它只对关键字参数有效。他将这些关键字参数收集到一个新的字典中,这个字典之后将能够用一般的字典工具处理

>>> def f(**args):print(args)
... 
>>> f(a=1, b=2)
{'a': 1, 'b': 2}

函数调用中:解包参数

基于位置的解包参数:

>>> def func(a, b, c, d):print(a, b, c, d)
... 
>>> args = (1, 2, 3, 4)
>>> func(*args)
1 2 3 4

基于关键字的解包参数:

>>> def func(a, b, c, d):print(a, b, c, d)
... 
>>> args = {'a': 1, 'b':s 2, 'c': 3, 'd':4}
>>> 
>>> func(**args)
1 2 3 4

同样,可以调用时能够以非常灵活的方式混用一般参数、基于位置的参数以及关键参数:

>>> func(*(1, 2), **{'d': 4, 'c': 3})
1 2 3 4
>>> func(1, *(2, 3), **{'d': 4})
1 2 3 4
>>> func(1, c=3, *(2,), **{'d': 4})
1 2 3 4
>>> func(1, *(2, 3), d=4)
1 2 3 4
>>> func(1, *(2,), c=3, **{'d': 4})
1 2 3 4
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值