参考:https://www.liaoxuefeng.com/wiki/1016959663602400/1017261630425888
根据函数(一)中的内容,不难发现python 函数的定义非常简单,且灵活度也非常大。
除了正常使用的必选参数外,还可以使用默认参数、可变参数与关键字参数,使得函数定义出来的接口,不但能处理复杂的信息,而且能简化使用者的代码。
位置参数
函数中,最常见的就是位置参数。
比如我们定义一个计算幂的函数。
def power(x):
return x * x
这个函数中,x就是一个位置参数。
当我们调用power时,必须传入这样一个位置参数。
但我们如果想要计算x**3
, x**4
呢?
总不至于重新设定power_3, power_4 吧。
最简单的方法,便是给函数添加一个新的参数。
def power(x, n):
return x ** n
但问题又来了,使用新函数时,旧的调用代码就失效了,有没有办法可以解决呢? 默认参数是个不错的选择。
默认参数
由于进行幂计算时,最常用的便是平方,也就是二次幂的计算。
因此python 也提供了默认参数的选择,我们可以将 n = 2,使得默认下,即在不做修改的前提下,默许 n = 2。
def power(x, n = 2):
return x ** n
而对于其他的幂,则可以明确传入power, power(2, 3)
除此之外需要注意两点。
1)必选参数必须在默认参数后面。如果必选参数在前,当我们将参数传入函数时,函数会默认将在先的参数传给默认参数,而这样子就可能会导致没有参数传入位置参数,导致函数出错。
2)在意识到第一点下,便可以极大地发挥默认参数的好处:将变化大的参数放在前面,变化小的放在后面。这样便可最大的简化使用者的代码。
比如需要编写一个学生注册的程序,需要录入学生的姓名,性别,年龄和籍贯。
对于大多数学生来说,年龄和籍贯都是固定的,因此可以将它们作为默认参数。
def enroll(name, gender, age = 6, city = 'Shang\'hai'):
print('name : ', name)
print('gender : ', gender)
print('age : ', age)
print('city : ', city)
enroll('Li Yang', 'M') # 调用
默认参数的“坑”
根据上面的演示,不难看出默认参数确实是一个方便我们书写代码的利器,合理的默认参数极大降低了使用者代码的重复度与复杂度。
def add_end(L=[]):
L.append('END')
return L
上面的代码,似乎设想的不错,在列表的末尾加上新的元素END,而不指定列表,也会默认定义L 为一个空列表。
但当我们多次调用函数,问题就来了。
>>> add_end()
['END']
>>> add_end()
['END', 'END']
每新调用一次函数,列表便在先前的基础上,新添加了一个元素。这和想要的不一样呀!
原因很简单,因为默认参数L 也是被一个变量定义,L指向该变量[],每次我们调用函数时,该变量都会被改变,因此在新的调用时L 的内容也会发生改变。只要我们不重新对L 赋值,则 L 会永远记得之前添加的元素。(还挺记仇?)
因此,一定要注意,默认参数必须指向不变对象。
通过将L 指向None
,这一不可变对象,即可实现。
def add_end(L = None):
if L is None: # 默认下 L 为None,执行语句
L = [] # 对L 重新赋值,使其为列表,巧妙避开默认参数的位置
L.append('END')
return L
正因此,python 设计了 tuple, str, None 这类的不可变对象,相当于给程序添加了保险,避免了因为对象内部数据的改变而影响了结果。
可变参数
可变参数不是说这个参数是可以变化的,而是指传入的参数的个数是可变的,可以是0个、1个、2个甚至无限个。
比如想要设计一个计算一组数字的平方之和的函数。
要定义这个函数,首先需要确定输入的参数。而由于可变参数的参数个数不固定,因此可以用list 或tuple 来传递参数。
def calc(numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
但问题时,在传入参数时,必须以list 或tuple 的形式。
而利用可变参数,便可以简化调用函数的方式。
>>> calc([1, 2, 3])
14
>>> calc(1, 2, 3)
14
定义可变参数
在参数前加上*
便可以定义可变参数。这时,输入的对象在函数内部便转换为了tuple。可以传入0或任意个参数。
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
如果想要传入list 或tuple呢?
一个方法是提取list或tuple 中的每一个元素,再传入函数中,但这未免有些麻烦。
还有一个方法是把list 或tuple 中的元素变成可变参数传入进去。即在该list 前加上*
。
list = [1, 2, 3]
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
print(calc(*list))
关键字参数
可变参数在函数调用时将参数自动组装为一个tuple,允许传入0个或任意个元素。而关键字参数则在函数内容组装为一个dict,也允许传入0个或任意个含参数名的参数。
关键字参数可以拓展函数的功能。提供可选择的参数选项。
在参数前加上**
便可定义关键字参数。
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
而关键字参数不是必须的。
>>> person('Michael', 30)
name: Michael age: 30 other: {}
也可以传入关键字参数,但需定义参数名及参数值。
且该关键字参数内容会保存在字典中。
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
如果想要传入dict 呢?
和可变参数同理,可以通过提取dict 中的每一个元素,再分别赋值给关键字参数的新参数名。
或者也可以在该dict 前加上**
,把这个字典中的全部键值对传入到关键字参数中。
extra = {'city': 'Beijing', 'job': 'Engineer'}
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
person('Jack', 24, **extra)
命名关键字参数
有的时候我们可能不希望传入无限制的关键字参数,这时,我们便可以使用命名关键字参数。
命名关键字参数和关键字参数**kw
不同,是通过在参数之间加上特殊分隔符*
,从而达到*
后面的参数为关键字参数。
def person(name, age, *, city, job):
print(name, age, city, job)
person('Jack', 24, city='Beijing', job='Engineer')
# 返回 Jack 24 Beijing Engineer
# 这时候如果输入其他的非指定的关键字参数
person('Jack', 24, city='Beijing', job='Engineer', zipp = 213213)
# 会报错
'''
TypeError: person() got an unexpected keyword argument 'zipp'
'''
如果函数中已经有了一个可变参数,则可变参数之后的参数被认为是命名关键字参数。
def person(name, age, *args, city, job):
print(name, age, args, city, job)
不同于位置参数,关键字参数必须传入参数名,否则会报错。因为如果不传入参数名,python 解释器会将参数默认为位置参数。
而命名关键字参数也可以和默认参数结合在一起使用,可以为关键字参数设定默认值,从而简化代码。
def person(name, age, *, city='Beijing', job):
print(name, age, city, job)
测试
>>> person('Jack', 24, job='Engineer')
Jack 24 Beijing Engineer
参数组合
有了这么多的参数种类,一个强大的程序必定是将这些参数组合在一起,从而实现强大的功能。
一共有五种参数。
- 必选参数(位置参数)
- 默认参数
- 可变参数
- 命名关键字参数
- 关键字参数
而定义这五种参数也需要遵循一定的顺序,必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
注意:虽然可以使用五种参数,但一般不建议使用过多的组合,否则函数的接口的可理解性将会很差。
网友评论