前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python中一定要注意的那些“坑”(三)

Python中一定要注意的那些“坑”(三)

作者头像
Python小屋屋主
发布2018-04-17 10:56:57
6770
发布2018-04-17 10:56:57
举报
文章被收录于专栏:Python小屋Python小屋

之前已发过的坑请参考Python函数默认值参数的2个坑Python编程中一定要注意的那些“坑”(一)Python编程中一定要注意的那些“坑”(二),今天再来填几个坑。

(1)有读者朋友在我编写的《Python程序设计(第2版)》第124页看到了一段关于函数默认值参数的代码:

>>> def demo(newitem, old_list=[]):

old_list.append(newitem)

return old_list

>>> demo('5', [1, 2, 3, 4])

[1, 2, 3, 4, '5']

>>> demo('aaa', ['a', 'b'])

['a', 'b', 'aaa']

>>> demo('a')

['a']

>>> demo('b') #意料之外的结果

['a', 'b']

虽然书中紧跟着给出了正确的代码和实现方式:

>>> def demo(newitem, old_list=None):

if old_list is None:

old_list = []

old_list.append(newitem)

return old_list

>>> demo('5', [1, 2, 3, 4])

[1, 2, 3, 4, '5']

>>> demo('aaa', ['a', 'b'])

['a', 'b', 'aaa']

>>> demo('a')

['a']

>>> demo('b')

['b']

但是书中并没有详细说明这里问题的原因是什么。实际上这是一个坑:当定义带有默认值参数的函数时,参数默认值只在函数定义时被解释一次,并被保存到函数的__defaults__成员中,这个__defaults__成员是一个元组,按顺序分别保存着所有默认值参数的当前值,当调用函数而不给默认值参数明确传递参数时,这些默认值参数就使用__defaults__成员中的当前值。因此,如果使用可变序列作为参数默认值并且在函数体内有为其增加元素或修改元素值的行为时,会对后续的调用产生影响。

(2)同样还是这本书上第130页有这样一段关于lambda表达式的代码:

>>> r = []

>>> for x in range(10):

r.append(lambda :x**2)

>>> r[0]() #意料之外的结果

81

>>> r[1]()

81

当然,书上紧跟着这段代码也给出了正确的实现方式:

>>> r = []

>>> for x in range(10):

r.append(lambda n=x: n**2)

>>> r[0]()

0

>>> r[1]()

1

>>> r[3]()

9

和上一个问题一样,书中虽然给出了正确的写法,但是并没有详细解释其中的道理,只是简单地说了一下变量作用域的问题。实际上,如果再深入挖掘一下,试一试下面的代码:

>>> g = lambda :n**2

>>> g()

Traceback (most recent call last):

File "<pyshell#105>", line 1, in <module>

g()

File "<pyshell#104>", line 1, in <lambda>

g = lambda :n**2

NameError: name 'n' is not defined

>>> n = 3

>>> g()

9

>>> n = 5

>>> g()

25

>>> n = 7

>>> g()

49

于是,可以得到这样一个结论,在上面第一段和最后一段代码中,lambda表达式中的x或n实际上是全局变量,它的值取决于调用lambda表达式时这个全局变量的当前值,注意是调用时。而中间一段代码通过参数默认值有效地避免了这样问题。正如本文第一个坑最后提到,函数参数的默认值是在函数定义时确定的。下面的代码或许能够更好地说明这个问题:

>>> n = 3

>>> def f(x=n):

print(x)

>>> f()

3

>>> n = 5

>>> f()

3

>>> n = 7

>>> f() #不影响函数调用结果

3

>>> def f(x=n): #函数参数x依赖于当前n的值

print(x)

>>> f()

7

(3)这个问题是读者看不懂书上关于(1)和(2)的代码,又在我的新书《Python可以这样学》里仍没有找到详细的解释之后到网上找的,但是看完之后发现更糊涂了。

>>> a = []

>>> b = {'num':0, 'sqrt':0}

>>> resource = [1, 2, 3]

>>> for i in resource:

b['num'] = i

b['sqrt'] = i * i

a.append(b)

>>> a #意料之外的结果

[{'num': 3, 'sqrt': 9}, {'num': 3, 'sqrt': 9}, {'num': 3, 'sqrt': 9}]

严格来说,最后这个问题和前面两个问题的性质也不一样,不是Python的坑,而是编程习惯不好造成。在代码中,首先b = {'num':0, 'sqrt':0}这一行是没有必要存在的;其次在循环中,不应该再次使用变量名b了,因为这会导致多次循环中修改同一个字典,这样的话后面的修改会覆盖前面的修改,从而导致错误结果。如果是我写的话,代码应该会写成下面的样子,不需要提前建好字典搭好框架,字典本来就是支持这样直接赋值添加元素的。

>>> a = []

>>> resource = [1, 2, 3]

>>> for i in resource:

b = dict()

b['num'] = i

b['sqrt'] = i * i

a.append(b)

>>> a #正确结果

[{'num': 1, 'sqrt': 1}, {'num': 2, 'sqrt': 4}, {'num': 3, 'sqrt': 9}]

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-02-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python小屋 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档