Python 函数参数

简述

在 Python 中,函数的定义非常简单,满足对应的语法格式要求即可。对于调用者来说,只需关注如何传递正确的参数,以及获取相应的返回值就足够了,无需了解函数的内部实现(除非想学习、跟踪源码)。

话虽如此,但对于函数的定义来说,灵活性非常高。除了常规定义的必选参数以外,还支持默认参数、可变参数、以及关键字参数。这样以来,不但能处理复杂的参数,还可以简化调用者的代码。

| 版权声明:一去、二三里,未经博主允许不得转载。

形参和实参

不止 Python,几乎所有的编程语言都会涉及两个术语:parameter 和 argument。那么,它们之间究竟有什么区别呢?

  • parameter 是指函数定义中的参数,而 argument 指的是函数调用时的实际参数。
  • 简略描述:parameter = 形参(formal parameter), argument = 实参(actual parameter)。

例如,定义一个简单的函数:

>>> def greet(param1, param2):
...     pass
... 
>>> 
>>> greet('Hello', 'Python')

其中,param1 和 param2 是函数的形参,而在函数 greet() 被调用时,传入的('Hello''Python')则是实参。

固定数量参数的函数

到目前为止,关于函数,我们介绍的都是固定数量的参数。来看一个简单的示例:

>>> def greet(say, msg):
...     print(say, msg)
... 
>>> 
>>> greet('Hello', 'Python')
Hello Python

这里,函数 greet() 有两个参数,用两个参数调用这个函数,运行得很顺利,不会有任何错误。

倘若,参数的个数不匹配,会发生什么?

>>> greet()  # 没有参数
...
TypeError: greet() missing 2 required positional arguments: 'say' and 'msg'
>>> 
>>> greet('Hi')  # 只有一个参数
...
TypeError: greet() missing 1 required positional argument: 'msg'

显然,解释器会发牢骚。但是,对 Python 来说,要解决这个问题简直是易如反掌,继续往下看!

默认参数

定义函数时,可以使用赋值运算符(=)为参数指定一个默认值。

注意: 如果参数没有默认值,在调用时必需为其指定一个值;如果参数有默认值,那么在调用时值是可选的,如果为其提供了一个值,将会覆盖默认值。

>>> def greet(say, name = 'James', msg = 'I am your biggest fan!'):
...     print(say, ',', name, ',', msg)
... 
>>> greet('Hi')  # 只提供强制性的参数
Hi , James , I am your biggest fan!
>>> 
>>> greet('Hi', 'Kobe')  # 给出一个可选参数
Hi , Kobe , I am your biggest fan!
>>> 
>>> greet('Hi', 'Kobe', 'I want to challenge you!')  # 给出所有参数
Hi , Kobe , I want to challenge you!

由于 say 没有默认值,所以必须指定;name、msg 有默认值,所以值是可选的。

函数中的所有参数都可以有默认值,但是,一旦存在一个默认参数,其右侧的所有参数也必须有默认值。也就是说,非默认参数不能在默认参数之后。

例如,将上面的函数定义为:

def greet(name = 'James', say):

就会引发错误:

SyntaxError: non-default argument follows default argument

关键字参数

当使用某些值调用函数时,这些值将根据其位置分配给参数。

例如,在上述函数 greet() 中,当使用 greet('Hi', 'Kobe') 调用它时,'Hi' 被赋值给参数 say,同样地,'Kobe' 被赋值给 name。

Python 允许使用 kwarg = value 格式的关键字参数调用函数:

>>> def greet(say, name = 'James'):
...     print(say, ',', name)
... 
>>> 
>>> greet(say = 'Hi', name = 'Kobe')  # 2 个关键字参数
Hi , Kobe
>>> 
>>> greet(name = 'Kobe', say = 'Hi')  # 2 个关键字参数(次序颠倒)
Hi , Kobe
>>> 
>>> greet('Hi', name = 'Kobe')  # 位置参数与关键字参数混合使用
Hi , Kobe

当以这种方式调用函数时,关键字参数必须在位置参数之后,所有传递的关键字参数都必须与函数接受的某个参数匹配,并且它们的顺序不重要。

例如,像下面这样调用,会引发错误:

>>> greet(name = 'Kobe', 'Hi')  # 关键字参数在位置参数之前
...
SyntaxError: positional argument follows keyword argument
>>>
>>> greet('Hi', na = 'Kobe')  # na 不匹配
...
TypeError: greet() got an unexpected keyword argument 'na'

可变参数

可变参数也被称为不定长参数,顾名思义,就是传入的参数个数是可变的,可以是任意个(0、1、2 … N)。

要定义可变参数,仅需在参数名之前添加一个星号(*)。在函数内部,这些参数被包装为一个 tuple。

注意:* 非彼 *,不要误认为是 C/C++ 中的指针。

>>> def greet(*names):
...     print(names)
... 
>>> 
>>> greet()  # 没有参数,返回空元组
()
>>>
>>> greet('Jordan', 'James', 'Kobe')
('Jordan', 'James', 'Kobe')

有时,必须在函数定义中使用位置参数以及可变参数,但位置参数始终必须在可变参数之前。

>>> def greet(say, *names):
...     print(say, names)
... 
>>> 
>>> greet('Hi')
Hi ()
>>> 
>>> greet('Hi', 'Jordan', 'James', 'Kobe')
Hi ('Jordan', 'James', 'Kobe')

通常情况下,可变参数会出现在形参列表的最后,因为它们会把传递给函数的所有剩余输入参数都收集起来。可变参数之后出现的任何形参都是“强制关键字”参数,这意味着,它们只能被用作关键字参数,而不能是位置参数。

>>> def greet(*names, sep = ','):
...     return sep.join(names)
... 
>>> 
>>> greet('Jordan', 'James', 'Kobe')
'Jordan,James,Kobe'
>>> 
>>> greet('Jordan', 'James', 'Kobe', sep = '/')  # 被用作关键字参数
'Jordan/James/Kobe'
>>>
>>> greet('Jordan', 'James', 'Kobe', '/')  # 被用作位置参数
'Jordan,James,Kobe,/'

任意关键字参数

还有一种机制,用于任意数量的关键字参数。为了做到这一点,使用双星号(**):

>>> def greet(**all_star):
...     print(all_star)
... 
>>> greet()  # 没有参数,返回空字典
{}
>>> 
>>> greet(name = 'James', age = 18)
{'name': 'James', 'age': 18}

当最后一个形式为 **msgs 的形参出现时,它将收到一个字典,其中包含所有关键字参数,除了与形参对应的关键字参数之外。还可以与 *names 的形参相结合(*names 必须出现在 **msgs 之前)。

例如,定义一个这样的函数:

>>> def greet(say, *names, **msgs):
...     print('--', say)
...     for name in names:
...         print(name)
...     print('-' * 40)
...     keys = sorted(msgs.keys())
...     for key in keys:
...         print(key, ':', msgs[key])
... 
>>> 
>>> greet('Hi', 'Jordan', 'James', 'Kobe', msg = 'I want to challenge you!', challenger = 'Waleon')
-- Hi
Jordan
James
Kobe
----------------------------------------
challenger : Waleon
msg : I want to challenge you!

注意: 在打印内容之前,通过对 msgs 字典的 keys() 方法的结果进行排序来创建关键字参数名称列表。如果没有这样做,则打印参数的顺序是未定义的。

对参数进行解包

正如“可变参数”那样,也可在函数调用中使用 * 操作符。只不过在这种情况下,与在函数定义中 * 的语义相反,参数将被解包而不是打包。

>>> def greet(name, age):
...     print(name, age)
... 
>>> 
>>> t = ('James', 18)
>>> greet(*t)
James 18

还有一种方式,几乎没有必要提到,这里也罗嗦一下:

>>> greet(t[0], t[1])
James 18

与解包相比,这种调用方式显然不舒适。另外,在一般情况下,调用 greet(t[0], t[1]) 几乎是徒劳的,因为长度是未知的。“未知”的意思是:长度只在运行时才知道,而不是在编写脚本时就知道。

同样地,字典也可以用 ** 操作符传递关键字参数:

>>> def greet(name, age = 18):
...     print(name, age)
... 
>>> 
>>> d = {'name':'James', 'age':32}
>>> greet(**d)
James 32
展开阅读全文
©️2020 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值