python写一个程序有一个函数输出一句话在程序启动时调用这个函数_不得不警惕的Python函数默认值 | 第81期...

适当设置一些函数的参数可以提升其灵活性和通用性,但是也带来了一个问题,即每次调用都要设置一大堆,有时还忘了该怎么设置参数。对于这种情况,函数默认值是一个很不错的语法点,也是Python开发者常用的功能。不过,对于一些特殊的默认值,可能不像我们想象中的那样表现。

53b7f77a097cdd6cf703084c27a8399b.png

披着羊皮的狼

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 一个正常的函数默认值

对于一个普通的函数,即不依赖全局变量的函数,我们常常要求这样的函数在输入相同的情况下,给出相同的输出。 默认值只是增加了运用的灵活性和通用性,却不应该改变这一个基本的函数设计思路。

77dce5359c0e0eb9c2b63543645704df.png

正常的调用状态

一个简单的例子如下

def func(x=2):    x = 1    return xprint(func(), func())

这段程序的输出为 1 1,就是非常符合我们的预期,也是正常函数应该有的特点,相同调用返回相同结果。但是从那个 “流行的问题” 中可以看出在有些默认值下,并不能保证这个最基本的函数特点。

3 复合类型的默认值

“流行的问题”和“正常的函数默认值”这两段程序最大的区别在于默认值的数据类型。Python函数是一个对象,并且在程序启动的时候就已经构造完成了。也就是说,函数的默认值在程序启动的时候就已经设置完成了,而不是等到调用的时候才被设置。 这对于基本的数据类型和常量数据是完全合理的,因为可以降低运行时的开销。但是,对于复合数据就有问题了,因为复合数据往往具有记忆性,比如 “流行问题” 中的 list 类型的默认值。

21ccc2eeaddd29ac963937d795b94376.png

有记忆的调用状态

正是复合数据类型的这种记忆性破坏了不同调用之间的独立性。 但是还有一个问题,按照这个逻辑,第一段程序的结果应该是 “B”,解释器为什么会给出 “D” 这个结果呢?

在第一段程序中,函数返回了这个复合结构的默认值变量,实际上返回的是一个 list对象 的引用,也就是说,无论这个函数被调用多少次,每次输出的都是指向同一块内存的引用。 而 print 函数运行的时候,这两个函数都运行结束了,自然而然,引用所指向的内容是两次函数调用之后的状态。

到此,关于那个“流行问题”的解释总算清晰了,但这并不是一个合理的语法点,也不值得利用。

4 一点建议

正是由于Python程序的灵活给了开发者极大的便利,这也对开发者的自觉性有了更高的要求。对于函数默认值的使用也应当有一些节制,事实上一个代码风格的提示工具已经指出了这一点。这里再提出以下几点建议:

1. 最好不要将函数默认值设置为复合类型

2. 必须设置复合类型的时候,应该保证该数据对象是只读的

3. 不要将复合类型的默认值参数返回到函数之外

----------------------

题外:

频道资源,可以私信关键字获取。

Python编程问题咨询,请发送关键字【咨询】

获取leetcode源代码,请发送关键字【leetcode】

获取书籍,请发送关键字【书籍】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值