适当设置一些函数的参数可以提升其灵活性和通用性,但是也带来了一个问题,即每次调用都要设置一大堆,有时还忘了该怎么设置参数。对于这种情况,函数默认值是一个很不错的语法点,也是Python开发者常用的功能。不过,对于一些特殊的默认值,可能不像我们想象中的那样表现。
1 一个流行的问题
前段时间,一个关于Python程序结果猜测的问题在互联网上流行,很多人都猜错了,而且还不知道到底错在哪里了。大家也可以来猜一猜这段代码的运行结果。
def func(x=[]): x.append(1) return xprint(func(), func())
有以下4个选项:
A. [1] [1]
B. [1] [1, 1]
C. [1, 1] [1]
D. [1, 1] [1, 1]
按照基本的程序设计思路来说,这段程序输出的结果应该是 “A”,但很显然这并不是正确的结果。如果我们再仔细地读一读这个函数的实现,可能会认为结果应该是 “B”。很可惜,Python解释器给出的结果是 “D”。 这就引出一个很值得思考的问题,即Python函数默认值的运行方式。
2 一个正常的函数默认值
对于一个普通的函数,即不依赖全局变量的函数,我们常常要求这样的函数在输入相同的情况下,给出相同的输出。 默认值只是增加了运用的灵活性和通用性,却不应该改变这一个基本的函数设计思路。
一个简单的例子如下
def func(x=2): x = 1 return xprint(func(), func())
这段程序的输出为 1 1,就是非常符合我们的预期,也是正常函数应该有的特点,相同调用返回相同结果。但是从那个 “流行的问题” 中可以看出在有些默认值下,并不能保证这个最基本的函数特点。
3 复合类型的默认值
“流行的问题”和“正常的函数默认值”这两段程序最大的区别在于默认值的数据类型。Python函数是一个对象,并且在程序启动的时候就已经构造完成了。也就是说,函数的默认值在程序启动的时候就已经设置完成了,而不是等到调用的时候才被设置。 这对于基本的数据类型和常量数据是完全合理的,因为可以降低运行时的开销。但是,对于复合数据就有问题了,因为复合数据往往具有记忆性,比如 “流行问题” 中的 list 类型的默认值。
正是复合数据类型的这种记忆性破坏了不同调用之间的独立性。 但是还有一个问题,按照这个逻辑,第一段程序的结果应该是 “B”,解释器为什么会给出 “D” 这个结果呢?
在第一段程序中,函数返回了这个复合结构的默认值变量,实际上返回的是一个 list对象 的引用,也就是说,无论这个函数被调用多少次,每次输出的都是指向同一块内存的引用。 而 print 函数运行的时候,这两个函数都运行结束了,自然而然,引用所指向的内容是两次函数调用之后的状态。
到此,关于那个“流行问题”的解释总算清晰了,但这并不是一个合理的语法点,也不值得利用。
4 一点建议
正是由于Python程序的灵活给了开发者极大的便利,这也对开发者的自觉性有了更高的要求。对于函数默认值的使用也应当有一些节制,事实上一个代码风格的提示工具已经指出了这一点。这里再提出以下几点建议:
1. 最好不要将函数默认值设置为复合类型
2. 必须设置复合类型的时候,应该保证该数据对象是只读的
3. 不要将复合类型的默认值参数返回到函数之外
----------------------
题外:
频道资源,可以私信关键字获取。
Python编程问题咨询,请发送关键字【咨询】
获取leetcode源代码,请发送关键字【leetcode】
获取书籍,请发送关键字【书籍】