
第19条 不要把函数返回的多个数值拆分到三个以上的变量中
unpacking机制允许Python函数返回一个以上的值(参见第6条)。假设现在要统计一群鳄鱼的各项指标,把每条鳄鱼的体长都保存在列表里。接着要编写函数,查出列表中最短与最长的鳄鱼。这可以用下面这种写法实现,它可以同时把这两个值都返回。

函数返回的其实是个元组(tuple
),同时返回的那两个值就是元组中的两个元素。程序通过把函数返回的元组赋给minimum
与maximum
这两个变量来拆解元组,相当于用这两个变量分别接收元组中的两个元素。下面举一个更简单的例子,来演示unpacking语句和返回多个值的函数是怎么使用的。

在返回多个值的时候,可以用带星号的表达式接收那些没有被普通变量捕获到的值(参见第13条)。例如,我们还需要写一个函数,用来计算每条鳄鱼的长度与这些鳄鱼的平均长度之比。该函数会把比值放在列表里返回,但我们在接收的时候,可以只接收最长与最短的那两条鳄鱼所对应的比值,而把中间那些鳄鱼的比值用带星号的写法总括。

假设现在需求又变了,我们这次还想知道平均长度、中位长度(长度的中位数)以及样本的总数。我们可以扩展原有的get_stats
函数,让它把这些指标也计算出来,然后一并通过元组返回给调用方,让调用方自己去拆分。

这样写有两个问题。首先,函数返回的五个值都是数字,所以很容易就会搞错顺序(例如,把平均数average
和中位数median
弄颠倒了),这可能让程序出现难以查找的bug。调用方同时要接收这么一大堆返回值,也特别容易出错。

第二个问题是,调用函数并拆分返回值的那行代码会写得比较长,所以按照PEP8风格指南,可能需要折行(参见第2条),这让代码看起来很别扭。

为避免这些问题,我们不应该把函数返回的多个值拆分到三个以上的变量里。一个三元组最多只拆成三个普通变量,或两个普通变量与一个万能变量(带星号的变量)。当然用于接收的变量个数也可以比这更少。假如要拆分的值确实很多,那最好还是定义一个轻便的类或namedtuple
(参见第37条),并让函数返回这样的实例。
要点
- 函数可以把多个值合起来通过一个元组返回给调用者,以便利用Python的unpacking机制去拆分。
- 对于函数返回的多个值,可以把普通变量没有捕获到的那些值全都捕获到一个带星号的变量里。
- 把返回的值拆分到四个或四个以上的变量是很容易出错的,所以最好不要那么写,而是应该通过小类或
namedtuple
实例完成。