Python——参数(1)

44 篇文章 0 订阅
44 篇文章 21 订阅
传递参数

之前说过参数是通过赋值来传递的。
下面是给函数传递参数时的一些简要的关键点:
1.参数的传递是通过自动将对象赋值给本地变量名来实现的。
2.在函数内部的参数名的赋值不会影响调用者

3.改变函数的可变对象参数的值也许会对调用者有影响。

===================================================================

参数和共享引用

考虑如下代码:

>>> def f(a):
	a=99

	
>>> b = 88
>>> f(b)
>>> print(b)
88
这里,在使用f(b)调用函数的时候,变量a赋值了对象88,但是,a只是存在于调用的函数之中。在函数中修改a对于调用函数的地方没有任何影响,它直接把本地变量a重置为一个完全不同的对象。

但是当参数传递像列表和字典这样的可修改对象的时候,需要注意,对这样的对象的原处修改可能在函数退出后依然有效,并由此影响到调用者。

>>> def changer(a,b):
	a = 2
	b[0] = 'spam'

	
>>> x = 1
>>> L = [1,2]
>>> changer(x,L)
>>> x,L
(1, ['spam', 2])
在这段代码中,changer函数给参数a赋值,并给参数b所引用的一个对象元素赋值。
最后,没有改变x的值,但改变了L的值。
因为x不能在原地修改,而L支持在原地修改。

如果不想函数内部在原处的修改影响传递给它的对象,那么,可以简单地创建一个明确的可变对象的拷贝。

L = [1,2]
changer(x,L[:])
如果不想改变传入的对象,无论函数是如何调用的,也同样可以在函数内部进行拷贝。
def changer(a,b):
	b = b[:]
	a=2
	b[0] = 'spam'
当然,这两种机制都不会阻止函数改变对象:这样做仅仅是防止了这些改变会影响调用者。
===================================================================
特定的参数匹配模型

Python提供了额外的工具,可以改变调用过程中,赋值时参数对象匹配在头部的参数名的优先级。这些工具都是可选的。

在默认情况下,参数是通过其位置进行匹配的,从左至右,而且必须精确地传递和函数头部参数名一样多的参数。还能够通过定义变量名进行匹配,默认参数值,以及对于额外参数的容器。

--------------------------------------------------------------------------------------------------------------------

基础知识

这是一些关于匹配模型的大纲:
1.位置:从左至右进行匹配
一般情况下,也是迄今为止最常使用的那种方法,是通过位置进行匹配把参数值传递给函数头部的参数名称,匹配顺序为从左至右。

2.关键字参数:通过参数名进行匹配
调用者可以定义哪一个参数接受这个值,通过在调用时使用参数的变量名,使用name=value的语法

3.默认参数:为没有传入值的参数定义参数值
如果调用时传入的值过于少的话,函数能够为参数定义接受的默认值,再一次使用语法name=value

4.Keywork-only参数:参数必须按照名称传递
在Python3.0中,函数也可以指定参数,参数必须用带有关键参数的名字(而不是位置)来传递。这样的参数通常用来定义实际参数以外的配置选项。

===================================================================

关键字参数和默认参数的实例

如果你没有使用任何特殊的匹配语法,Python默认会通过位置从左至右匹配变量名。例如,如果定义了一个需要三个参数的函数,必须使用三个参数对它进行调用:

>>> def f(a,b,c):
	print(a,b,c)

	
>>> f(1,2,3)
1 2 3
这里依次按照位置传递值:a匹配到1,b匹配到2,依次类推。

--------------------------------------------------------------------------------------------------------------------

关键字参数

关键字参数允许通过变量名进行匹配,而不是位置

>>> f(c=3,b=2,a=1)
1 2 3
例如,这个调用中,c=3,意味着将3传递给参数c。注意到这时关键字参数使用时参数从左至右的关系不再重要了,因为参数是根据变量名进行传递的,而不是根据位置。

甚至在一个调用中混合使用基于位置的参数和基于关键字的参数都可以。在这种情况下,所有基于位置的参数先按照从左至右的顺序匹配头部的参数,之后再进行基于变量名进行关键字的匹配。

>>> f(1,c=3,b=2)
1 2 3
为什么使用这种工具呢?关键字参数在Python中扮演了两个典型的角色,首先,它们使调用显得更文档化一些(假设使用了比a,b,c更好的参数名),例如:
func(name='Bob',age=40,job='dev')
这种形式的调用比直接进行一个由逗号分隔的三个值的调用明了得多:关键字参数在调用中起到了数据标签的作用。
------ --------------------------------------------------------------------------------------------------------------

默认参数

默认参数允许创建函数可选的参数。如果没有传入值的话,在函数运行前,参数就被赋了默认值。例如,这个函数需要一个参数和两个默认参数:

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

	
>>> f(1)
1 2 3
>>> f(a=1)
1 2 3
当调用这个函数的时候,必须为a提供值,无论是通过位置参数还是关键字参数来实现。然而,为b和c提供值是可选的,如果不给它们赋值,它们会默认分别赋值2和3.

当给函数传递两个值的时候,只有c得到默认值,并且当有三个值传递时,不会使用默认值:

>>> f(1,4)
1 4 3
>>> f(1,4,5)
1 4 5
最后,这是关键字和默认参数一起使用后的情况:

>>> f(1,c=6)
1 2 6
这里,a通过位置得到了1,c通过关键字得到了6,而b在两者之间,通过默认值获得了2.
------ --------------------------------------------------------------------------------------------------------------
关键字参数和默认参数的混合

下述是一个关键字和默认参数在实际应用中稍复杂的例子。在这个例子中,调用者必须至少传递两个参数去匹配spam和eggs,其他是可选的。

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

	
>>> func(1,2)
(1, 2, 0, 0)
>>> func(1,ham=1,eggs=0)
(1, 0, 0, 1)
>>> func(spam=1,eggs=0)
(1, 0, 0, 0)
>>> func(toast=1,eggs=2,spam=3)
(3, 2, 1, 0)
>>> func(1,2,3,4)
(1, 2, 3, 4)
===================================================================
任意参数的实例

最后两种匹配扩展,*和**,是让函数支持接收任意数目的参数的。它们都可以出现在函数定义或是函数调用中。

--------------------------------------------------------------------------------------------------------------------

收集参数

第一种用法:在函数定义中,在元祖中收集不匹配的位置参数。

>>> def f(*args):
	print(args)
当这个函数调用时,Python将所有位置相关的参数收集到一个新的元祖中,并将这个元祖赋值给变量args。
因为它是一个一般的元祖对象,能够索引或在一个for循环中进行步进。

>>> f()
()
>>> f(1)
(1,)
>>> f(1,2,3,4)
(1, 2, 3, 4)
**特性类似,但是它只对关键字参数有效。将这些关键字参数传递给一个新的字典,这个字典之后将能够通过一般的字典工具进行处理。如下所示:
>>> def f(**args):
	print(args)

	
>>> f()
{}
>>> f(a=1,b=2)
{'a': 1, 'b': 2}
最后,函数头部能够混合一般参数、*参数以及**去实现更加灵活的调用方式。例如:
>>> def f(a,*pargs,**kargs):
	print(a,pargs,kargs)

	
>>> f(1,2,3,x=1,y=2)
1 (2, 3) {'y': 2, 'x': 1}
在上述代码中,1按照位置传递给a,2和3收集到pargs位置元祖中,x和y放入kargs关键字字典中。
------ --------------------------------------------------------------------------------------------------------------

解包参数

在最新的Python版本中,我们在调用函数时能够使用*语法。在这种情况下,它与函数定义的意思相反。它会解包参数的集合,而不是创建参数的集合。例如,我们能够通过一个元祖给一个函数传递四个参数,并且让Python将它们解包成不同的参数。

>>> def func(a,b,c,d):
	print(a,b,c,d)

	
>>> args=(1,2)
>>> args += (3,4)
>>> func(*args)
1 2 3 4
相似地,在函数调用时,**会以键/值对的形式解包一个字典,使其成为独立的关键字参数。
>>> args={'a':1,'b':2,'c':3}
>>> args['d']=4
>>> func(**args)
1 2 3 4
另外,我们在调用中能够以非常灵活的方式混合普通的参数、基于位置的参数以及关键字参数。
>>> func(*(1,2),**{'d':4,'c':4})
1 2 4 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
在编写脚本时,当我们不能预测将要传入参数的参数的数量的时候,这种代码是很方便的。
看一个收集参数和解包混合使用的例子:

>>> def f(a=0,*b,**c):
	print(a,b,c)

>>> f(1,2,e=1,f=2)
1 (2,) {'f': 2, 'e': 1}
>>> f(1,2,*(3,4),e=3,**{'f':2})
1 (2, 3, 4) {'f': 2, 'e': 3}
在这个例子的第二次调用,f(1,2,*(3,4),e=3,**{'f':2})先解包出来,等价于:f(1,2,3,4,e=3,f=2)
===================================================================

Python3.0 Keyword-Only参数

Python3.0把函数头部的排序规则通用化了,允许我们指定keywork-only参数——即必须只按照关键字传递,并且不会由一个位置参数来填充的参数。
从语法上讲,keyword-only参数编码为命名的参数,出现在参数列表中的*args之后。所有这些参数都必须在调用中使用关键字参数语法来传递。

>>> def kwonly(a,*b,c):
	print(a,b,c)

	
>>> kwonly(1,2,c=3)
1 (2,) 3
>>> kwonly(a=1,c=3)
1 () 3
>>> kwonly(1,2,3)
Traceback (most recent call last):
  File "<pyshell#82>", line 1, in <module>
    kwonly(1,2,3)
TypeError: kwonly() missing 1 required keyword-only argument: 'c'
上述代码中,a可能按照名称或位置传递,b收集任何额外的位置参数,并且c必须只按照关键字传递。

我们也可以在参数列表中使用一个*字符,来表示一个函数不会接受一个变量长度的参数列表,而是仍然期待跟在*后面的所有参数都作为关键字传递。如下述代码,a可能再次按照位置或名称传递,但b和c必须按照关键字传递,不允许其他额外的位置传递。

>>> def kwonly(a,*,b,c):
	print(a,b,c)

>>> kwonly(1,c=3,b=2)
1 2 3
>>> kwonly(c=3,b=2,a=1)
1 2 3
>>> kwonly(1,2,3)
Traceback (most recent call last):
  File "<pyshell#88>", line 1, in <module>
    kwonly(1,2,3)
TypeError: kwonly() takes 1 positional argument but 3 were given

仍然可以对keyword-only参数使用默认值,即便他们出现在函数头部的*的后面。
>>> def kwonly(a,*,b='spam',c='ham'):
	print(a,b,c)

	
>>> kwonly(1)
1 spam ham
>>> kwonly(1,c=3)
1 spam 3
>>> kwonly(a=1)
1 spam ham
>>> kwonly(c=3,b=2,a=1)
1 2 3
>>> kwonly(1,2,3)
Traceback (most recent call last):
  File "<pyshell#98>", line 1, in <module>
    kwonly(1,2,3)
TypeError: kwonly() takes 1 positional argument but 3 were given
在上述代码中,a可能按照名称或者位置传递,而b和c是可选的,但是如果使用的话必须按照关键字传递

实际上,带有默认值的keyword-only参数都是可选的,但是,那些没有默认值的keyword-only参数真正地变成了函数必需的keyword-only参数:
>>> def kwonly(a,*,b,c='spam'):
	print(a,b,c)

	
>>> kwonly(1,b='eggs')
1 eggs spam
>>> kwonly(1,c='eggs')
Traceback (most recent call last):
  File "<pyshell#104>", line 1, in <module>
    kwonly(1,c='eggs')
TypeError: kwonly() missing 1 required keyword-only argument: 'b'
>>> def kwonly(a,*,b=1,c,d=2):
	print(a,b,c,d)

>>> kwonly(3,c=4)
3 1 4 2
>>> kwonly(3,c=4,b=5)
3 5 4 2
>>> kwonly(3)
Traceback (most recent call last):
  File "<pyshell#112>", line 1, in <module>
    kwonly(3)
TypeError: kwonly() missing 1 required keyword-only argument: 'c'
>>> kwonly(1,2,3)
Traceback (most recent call last):
  File "<pyshell#113>", line 1, in <module>
    kwonly(1,2,3)
TypeError: kwonly() takes 1 positional argument but 3 were given
------ --------------------------------------------------------------------------------------------------------------
排序规则

注意,keyword-only参数必须在一个单个星号后面指定,而不是两个星号——命名的参数不能出现在**args任意关键字形式的后面,并且一个**不能独自出现在参数列表中。这两种做法都将产生语法错误:

>>> def kwonly(a,**pargs,b,c):
	
SyntaxError: invalid syntax

>>> def kwonly(a,**,b,c)
SyntaxError: invalid syntax
这意味着,在一个函数头部,keyword-only参数必须编写在**args任意关键字形式之前,且在*args任意位置形式之后,当二者都有的时候。无论何时,一个参数名称出现在*args之前,它可能是默认位置参数,而不是keyword-only参数

>>> def f(a,*b,**d,c=6):
	
SyntaxError: invalid syntax
>>> def f(a,*b,c=6,**d):print(a,b,c,d)

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

>>> f(1,2,3,x=4)
1 (3,) 2 {'x': 4}
实际上,在函数调用中,类似的排序规则也是成立的:当传递keyword-only参数的时候,它们必须出现在一个**args形式之前。keyword-only参数可以编写在*args之前或者之后,并且可能包含在**args中:
>>> def f(a,*b,c=6,**d):print(a,b,c,d)

>>> f(1,*(2,3),**dict(x=4,y=5))
1 (2, 3) 6 {'y': 5, 'x': 4}
>>> f(1,*(2,3),**dict(x=4,y=5),c=7)
SyntaxError: invalid syntax
>>> f(1,*(2,3),c=7,**dict(x=4,y=5))
1 (2, 3) 7 {'y': 5, 'x': 4}
>>> f(1,c=7,*(2,3),**dict(x=4,y=5))
1 (2, 3) 7 {'y': 5, 'x': 4}
>>> f(1,*(2,3),**dict(x=4,y=5,c=7))
1 (2, 3) 7 {'y': 5, 'x': 4}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值