这一节带小伙伴们来深入了解一下 Python 函数的参数,主要包括形参和实参、参数可以是任何类型、位置参数、关键字参数、参数默认值等。
Python 形参和实参
在 Python 中,函数的参数可以分为 2 种:形参和实参。其中,函数在定义时的参数叫做 “形参”(形式上的参数),而函数在调用时的参数叫做 “实参”(实际上的参数)。
示例 1:使用形参和实参
def get_sum(a, b): # a 和 b 是形参
return a + b
result = get_sum(10, 20) # 10 和 20 是实参
print(result)
运行结果如下。
30
分析:
从名字上就可以很容易区分,形参本质上是一个变量,实参本质上是一个数值。我们调用函数时,其实就是把实参作为值,然后赋值给形参。是不是感觉很熟悉?其实这个跟变量的赋值是一样的,非常的简单。
对于 getsum(10, 20) 来说,它其实就是将 10 赋值给 a,并且将 20 赋值给 b,此时相当于执行下面代码。
a = 10
b = 20
函数参数可以是任何类型
之前接触的参数都是一些基本类型,比如数字、字符串等。实际上在 Python 中,所有数据类型都可以作为函数的参数,包括列表、元组、字典、集合等。
示例 2:列表作为函数参数
def get_sum(nums):
n = 0
for num in nums:
n += num
return n
nums = [3, 9, 1, 12, 50, 21]
result = get_sum(nums)
print(result)
运行结果如下。
96
分析:
上面示例定义了一个函数 getsum(),用于求列表所有元素之和。其中,函数的参数是一个列表。
示例 3:字典作为函数参数
def exchange(d):
lst = []
for key, value in d.items():
lst.append((key, value))
return lst
users = {'Jack': 1001, 'Lucy': 1002, 'Tony': 1003}
result = exchange(users)
print(result)
运行结果如下。
[('Jack', 1001), ('Lucy', 1002), ('Tony', 1003)]
分析:
上面示例定义了一个函数 exchange(),用于将一个字典转换成一个元组型的列表。其中,函数的参数是一个字典。
Python 位置参数
默认情况下,Python 函数定义的参数都属于 “位置参数(positional arguments)” 。其中,位置参数具有以下两个特点。
- 个数相同:实参和形参的个数必须相同。
- 位置相同:实参和形参的位置必须相同。
此外,位置参数也叫做 “必要参数” 或 “必须参数”。很多地方的叫法不同,我们要知道这几种叫法指的是一个意思。再次强调一次,一般情况下定义的参数都是位置参数。
示例 4:参数个数不一致
def get_info(name, age, gender):
result = name + str(age) + gender
print(result)
get_info('Jack', 18)
运行结果如下。
(报错)TypeError: get_info() missing 1 required positional argument: 'gender'
分析:
当形参个数跟实参个数不一致时,会抛出异常(即报错)。因此我们一定要保证函数调用时的实参个数跟函数定义时的形参个数相同。
示例 5:参数位置不一致
def get_info(name, age, gender):
result = name + str(age) + gender
print(result)
get_info('Jack', 'male', 18)
运行结果如下。
(报错)TypeError: can only concatenate str (not "int") to str
分析:
当形参位置跟实参位置不一致时,如果数据类型不同,也会抛出异常。如果数据类型相同,那么不会抛出异常,但是会产生跟预期结果不符的问题。
因此在实际开发中,我们也要保证 “函数调用时的实参位置” 与 “函数定义时的形参位置” 一致,不然就会产生预想不到的问题。
Python 关键字参数
在 Python 中,如果想要使得 “函数调用时的实参位置” 跟 “函数定义时的形参位置” 顺序不一致时,也能达到相同的预期结果,应该怎么做呢?这个时候可以使用关键字参数(keyword arguments)来实现。
示例 6:使用关键字参数
def get_info(name, age, gender):
result = name + str(age) + gender
print(result)
get_info(name='jack', gender='male', age=18)
运行结果如下。
jack18male
分析:
在上面示例中,虽然实参的顺序和形参的顺序不一致,但是使用了关键字参数之后,运行结果和预期结果是一样的。所谓的关键字参数,指的是在函数调用时,直接给与形参相同的名字进行赋值。
关键字参数非常有用,它最大的好处就是:只要把参数名写对就可以了,避免还需要去对比各个参数位置的麻烦。在实际开发中,如果一个函数的参数比较少,我们使用一般参数(也就是位置参数)就可以了。但是如果一个函数参数非常多,此时关键字参数就非常有用了。
Python 参数默认值
在 Python 函数 中,我们可以给形参设置一个默认值。当函数调用时,如果该参数没有传实参进来,就会使用指定的默认值作为这个参数的值。
默认参数(default arguments)也叫做 “带默认值的参数”,你要清楚这两种叫法指的是同一个东西。在函数定义时,对于需要设置默认值的参数,我们必须将它们放在所有参数的最后,不然就会产生语法错误。
示例 7:使用参数默认值
def ball(color, radius=10):
print(color, radius)
ball('red')
运行结果如下。
red 10
分析:
在这个示例中,由于给参数 radius 设置了一个默认值 10,因此在调用函数时,如果没有给参数 radius 传入值,那么函数就会使用这个默认值。
示例 8:带默认值参数的位置
def ball(radius=10, color):
print(radius, color)
ball('red')
运行结果如下。
(报错)SyntaxError: parameter without a default follows parameter with a default
分析:
这里要清楚一点,对于需要设置默认值的参数,一定要放在不需要设置默认值的参数的后面,否则就会报错。原因其实很简单,如果写成上面示例这样,ball('red') 其实是将 'red' 赋值给第 1 个参数 radius,此时 radius 的值是 'red'。这样就会导致第 2 个参数 color 没有传入值,所以就会报错。
示例 9:覆盖默认值
def ball(color, radius=10):
print(color, radius)
ball('red', 20)
运行结果如下。
red 20
分析:
从结果可以看出来,“实参的值” 会覆盖 “形参的默认值”,因此 radius 最终的值是 20,而不是 10。
有些时候想查看函数所有参数的默认值,应该怎么做呢?我们可以使用 “函数名.__defaults__” 这种方式。
示例 10:查看默认值
def ball(color, radius=10):
print(color, radius)
print(ball.__defaults__)
运行结果如下。
(10,)
分析:
__defaults__ 得到的是一个元组。此外要注意一点,如果使用可变对象(比如列表)作为函数参数的默认值时,多次调用会导致一些问题,请看下面示例。
示例 11:默认值是列表
def fn(colors=['red', 'green', 'blue']):
print(colors)
colors.append('red')
fn()
运行结果如下。
['red', 'green', 'blue']
分析:
如果只调用一次 fn(),结果是没有问题的。但是如果连续多次调用 fn(),又会怎么样呢?比如调用 2 次 fn(),此时运行结果如下。
['red', 'green', 'blue']
['red', 'green', 'blue', 'red']
如果调用 3 次 fn(),此时运行结果如下。
['red', 'green', 'blue']
['red', 'green', 'blue', 'red']
['red', 'green', 'blue', 'red', 'red']
从上面结果来看,这显然不是我们想要的结果。为了防止出现这种情况,最好使用 None 作为可变对象的默认值,并且还需要进行代码检查。
示例 12:改进后的代码
def fn(colors=None):
if colors == None:
colors = ['red', 'green', 'blue']
print(colors)
colors.append('red')
fn()
fn()
fn()
运行结果如下。
['red', 'green', 'blue']
['red', 'green', 'blue']
['red', 'green', 'blue']
分析:
对代码改进之后,不管我们调用多少次 fn(),此时 colors 的默认值其实都是 ['red', 'green', 'blue']。
最后,对于函数参数的默认值,我们可以总结出以下 3 点。
- 指定默认值的参数必须放在所有参数的最后,不然就会报错。
- 可以使用 “函数名.__defaults__” 来查看所有参数的默认值。
- 参数的默认值不要直接指定一个可变对象,而是应该采用变通方法。
