之前已發過的坑請參考Python函數默認值參數的2個坑,Python編程中一定要注意的那些“坑”(一)和Python編程中一定要注意的那些“坑”(二),今天再來填幾個坑。
python遇到問題及解決方案?(1)有讀者朋友在我編寫的《Python程序設計(第2版)》第124頁看到了一段關于函數默認值參數的代碼:
>>> def demo(newitem, old_list=[]):
python致命缺點、 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}]
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态