
Python程序員鮮為人知但你應(yīng)該知道的17個問題
這篇文章主要介紹了Python程序員代碼編寫時(shí)應(yīng)該避免的17個“坑”,也可以說成Python程序員代碼編寫時(shí)應(yīng)該避免的17個問題,需要的朋友可以參考下
一、不要使用可變對象作為函數(shù)默認(rèn)值
In [2]: my_list = append_to_list(1)
In [3]: my_list
Out[3]: [1]
In [4]: my_other_list = append_to_list(2)
In [5]: my_other_list
Out[5]: [1, 2] # 看到了吧,其實(shí)我們本來只想生成[2] 但是卻把第一次運(yùn)行的效果頁帶了進(jìn)來
In [6]: import time
In [7]: def report_arg(my_default=time.time()):
...: print(my_default)
...:
In [8]: report_arg() # 第一次執(zhí)行
1399562371.32
In [9]: time.sleep(2) # 隔了2秒
In [10]: report_arg()
1399562371.32 # 時(shí)間竟然沒有變
這2個例子說明了什么? 字典,集合,列表等等對象是不適合作為函數(shù)默認(rèn)值的. 因?yàn)檫@個默認(rèn)值實(shí)在函數(shù)建立的時(shí)候就生成了, 每次調(diào)用都是用了這個對象的”緩存”. 我在上段時(shí)間的分享python高級編程也說到了這個問題,這個是實(shí)際開發(fā)遇到的問題,好好檢查你學(xué)過的代碼, 也許只是問題沒有暴露
可以這樣改:
二、生成器不保留迭代過后的結(jié)果
In [13]: 2 in gen
Out[13]: True
In [14]: 3 in gen
Out[14]: True
In [15]: 1 in gen
Out[15]: False # 1為什么不在gen里面了? 因?yàn)檎{(diào)用1->2,這個時(shí)候1已經(jīng)不在迭代器里面了,被按需生成過了
In [20]: gen = (i for i in range(5))
In [21]: a_list = list(gen) # 可以轉(zhuǎn)化成列表,當(dāng)然a_tuple = tuple(gen) 也可以
In [22]: 2 in a_list
Out[22]: True
In [23]: 3 in a_list
Out[23]: True
In [24]: 1 in a_list # 就算循環(huán)過,值還在
Out[24]: True
三、lambda在閉包中會保存局部變量
In [30]: for l in my_list:
....: print(l())
....:
4
4
4
4
4
這個問題還是上面說的python高級編程中說過具體原因. 其實(shí)就是當(dāng)我賦值給my_list的時(shí)候,lambda表達(dá)式就執(zhí)行了i會循環(huán),直到 i =4,i會保留
但是可以用生成器
In [32]: for l in my_gen:
....: print(l())
....:
0
1
2
3
4
也可以堅(jiān)持用list:
In [34]: for l in my_list:
....: print(l())
....:
0
1
2
3
4
有點(diǎn)不好懂是吧,在看看python的另外一個魔法:
In [36]: groupby(range(9), 3)
Out[36]: [(0, 1, 2), (3, 4, 5), (6, 7, 8)]
一個分組的函數(shù),看起來很不好懂,對吧? 我們來解析下這里
四、在循環(huán)中修改列表項(xiàng)
In [45]: for i in a:
....: if not i % 2:
....: a.remove(i)
....:
In [46]: a
Out[46]: [1, 3, 5] # 沒有問題
In [50]: b = [2, 4, 5, 6]
In [51]: for i in b:
....: if not i % 2:
....: b.remove(i)
....:
In [52]: b
Out[52]: [4, 5] # 本來我想要的結(jié)果應(yīng)該是去除偶數(shù)的列表
思考一下,為什么 – 是因?yàn)槟銓α斜淼膔emove,影響了它的index
In [54]: for index, item in enumerate(b):
....: print(index, item)
....: if not item % 2:
....: b.remove(item)
....:
(0, 2) # 這里沒有問題 2被刪除了
(1, 5) # 因?yàn)?被刪除目前的列表是[4, 5, 6], 所以索引list[1]直接去找5, 忽略了4
(2, 6)
五、IndexError - 列表取值超出了他的索引數(shù)
In [56]: my_list[5] # 根本沒有這個元素
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-56-037d00de8360> in <module>()
----> 1 my_list[5]
IndexError: list index out of range # 拋異常了
In [57]: my_list[5:] # 但是可以這樣, 一定要注意, 用好了是trick,用錯了就是坑啊
Out[57]: []
六、重用全局變量
In [59]: var = 'global' # 后賦值
In [60]: my_func() # 反正只要調(diào)用函數(shù)時(shí)候變量被定義了就可以了
global
In [61]: def my_func():
....: var = 'locally changed'
....:
In [62]: var = 'global'
In [63]: my_func()
In [64]: print(var)
global # 局部變量沒有影響到全局變量
In [65]: def my_func():
....: print(var) # 雖然你全局設(shè)置這個變量, 但是局部變量有同名的, python以為你忘了定義本地變量了
....: var = 'locally changed'
....:
In [66]: var = 'global'
In [67]: my_func()
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-67-d82eda95de40> in <module>()
----> 1 my_func()
<ipython-input-65-0ad11d690936> in my_func()
1 def my_func():
----> 2 print(var)
3 var = 'locally changed'
4
UnboundLocalError: local variable 'var' referenced before assignment
In [68]: def my_func():
....: global var # 這個時(shí)候得加全局了
....: print(var) # 這樣就能正常使用
....: var = 'locally changed'
....:
In [69]: var = 'global'
In [70]:
In [70]: my_func()
global
In [71]: print(var)
locally changed # 但是使用了global就改變了全局變量
七、拷貝可變對象
In [73]: my_list1
Out[73]: [[1, 2, 3], [1, 2, 3]]
In [74]: my_list1[1][0] = 'a' # 我只修改子列表中的一項(xiàng)
In [75]: my_list1
Out[75]: [['a', 2, 3], ['a', 2, 3]] # 但是都影響到了
In [76]: my_list2 = [[1, 2, 3] for i in range(2)] # 用這種循環(huán)生成不同對象的方法就不影響了
In [77]: my_list2[1][0] = 'a'
In [78]: my_list2
Out[78]: [[1, 2, 3], ['a', 2, 3]]
八、python多繼承(C3)
In [2]: class B(object):
...: def foo(self):
...: print("class B")
...:
In [3]: class C(A, B):
...: pass
...:
In [4]: C().foo()
class A # 例子很好懂, C繼承了A和B,從左到右,發(fā)現(xiàn)A有foo方法,返回了
看起來都是很簡單, 有次序的從底向上,從前向后找,找到就返回. 再看例子:
In [6]: class B(A):
...: pass
...:
In [7]: class C(A):
...: def foo(self):
...: print("class C")
...:
In [8]: class D(B,C):
...: pass
...:
In [9]: D().foo()
class C # ? 按道理, 順序是 D->B->A,為什么找到了C哪去了
這也就涉及了MRO(Method Resolution Order):
簡單的理解其實(shí)就是新式類是廣度優(yōu)先了, D->B, 但是發(fā)現(xiàn)C也是繼承A,就先找C,最后再去找A
九、列表的+和+=, append和extend
In [18]: a_list += [1]
In [19]: print('ID (+=):', id(a_list))
('ID (+=):', 4481323592) # 使用+= 還是在原來的列表上操作
In [20]: a_list = a_list + [2]
In [21]: print('ID (list = list + ...):', id(a_list))
('ID (list = list + ...):', 4481293056) # 簡單的+其實(shí)已經(jīng)改變了原有列表
In [28]: a_list = []
In [29]: id(a_list)
Out[29]: 4481326976
In [30]: a_list.append(1)
In [31]: id(a_list)
Out[31]: 4481326976 # append 是在原有列表添加
In [32]: a_list.extend([2])
In [33]: id(a_list)
Out[33]: 4481326976 # extend 也是在原有列表上添加
十、datetime也有布爾值
這是一個坑
In [35]: print('"datetime.time(0,0,0)" (Midnight) ->', bool(datetime.time(0,0,0)))
('"datetime.time(0,0,0)" (Midnight) ->', False)
In [36]: print('"datetime.time(1,0,0)" (1 am) ->', bool(datetime.time(1,0,0)))
('"datetime.time(1,0,0)" (1 am) ->', True)
十一、'==' 和 is 的區(qū)別
我的理解是”is”是判斷2個對象的身份, ==是判斷2個對象的值
In [38]: b = 1
In [39]: print('a is b', bool(a is b))
('a is b', True)
In [40]: c = 999
In [41]: d = 999
In [42]: print('c is d', bool(c is d))
('c is d', False) # 原因是python的內(nèi)存管理,緩存了-5 - 256的對象
In [43]: print('256 is 257-1', 256 is 257-1)
('256 is 257-1', True)
In [44]: print('257 is 258-1', 257 is 258 - 1)
('257 is 258-1', False)
In [45]: print('-5 is -6+1', -5 is -6+1)
('-5 is -6+1', True)
In [46]: print('-7 is -6-1', -7 is -6-1)
('-7 is -6-1', False)
In [47]: a = 'hello world!'
In [48]: b = 'hello world!'
In [49]: print('a is b,', a is b)
('a is b,', False) # 很明顯 他們沒有被緩存,這是2個字段串的對象
In [50]: print('a == b,', a == b)
('a == b,', True) # 但他們的值相同
# But, 有個特例
In [51]: a = float('nan')
In [52]: print('a is a,', a is a)
('a is a,', True)
In [53]: print('a == a,', a == a)
('a == a,', False) # 亮瞎我眼睛了~
十二、淺拷貝和深拷貝
我們在實(shí)際開發(fā)中都可以向?qū)δ沉斜淼膶ο笞鲂薷?但是可能不希望改動原來的列表. 淺拷貝只拷貝父對象,深拷貝還會拷貝對象的內(nèi)部的子對象
In [66]: list2 = list1 # 就是個引用, 你操作list2,其實(shí)list1的結(jié)果也會變
In [67]: list3 = list1[:]
In [69]: import copy
In [70]: list4 = copy.copy(list1) # 他和list3一樣 都是淺拷貝
In [71]: id(list1), id(list2), id(list3), id(list4)
Out[71]: (4480620232, 4480620232, 4479667880, 4494894720)
In [72]: list2[0] = 3
In [73]: print('list1:', list1)
('list1:', [3, 2])
In [74]: list3[0] = 4
In [75]: list4[1] = 4
In [76]: print('list1:', list1)
('list1:', [3, 2]) # 對list3和list4操作都沒有對list1有影響
# 再看看深拷貝和淺拷貝的區(qū)別
In [88]: from copy import copy, deepcopy
In [89]: list1 = [[1], [2]]
In [90]: list2 = copy(list1) # 還是淺拷貝
In [91]: list3 = deepcopy(list1) # 深拷貝
In [92]: id(list1), id(list2), id(list3)
Out[92]: (4494896592, 4495349160, 4494896088)
In [93]: list2[0][0] = 3
In [94]: print('list1:', list1)
('list1:', [[3], [2]]) # 看到了吧 假如你操作其子對象 還是和引用一樣 影響了源
In [95]: list3[0][0] = 5
In [96]: print('list1:', list1)
('list1:', [[3], [2]]) # 深拷貝就不會影響
十三、bool其實(shí)是int的子類
In [98]: True + True
Out[98]: 2
In [99]: 3 * True + True
Out[99]: 4
In [100]: 3 * True - False
Out[100]: 3
In [104]: True << 10
Out[104]: 1024
十五、元組是不是真的不可變?
In [112]: tup[0] += [1]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-112-d4f292cf35de> in <module>()
----> 1 tup[0] += [1]
TypeError: 'tuple' object does not support item assignment
In [113]: tup
Out[113]: ([1],) # 我靠 又是亮瞎我眼睛,明明拋了異常 還能修改?
In [114]: tup = ([],)
In [115]: tup[0].extend([1])
In [116]: tup[0]
Out[116]: [1] # 好吧,我有點(diǎn)看明白了, 雖然我不能直接操作元組,但是不能阻止我操作元組中可變的子對象(list)
這里有個不錯的解釋Python's += Is Weird, Part II :
In [118]: my_tup += (4,)
In [119]: my_tup = my_tup + (5,)
In [120]: my_tup
Out[120]: (1, 4, 5) # ? 嗯 不是不能操作元組嘛?
In [121]: my_tup = (1,)
In [122]: print(id(my_tup))
4481317904
In [123]: my_tup += (4,)
In [124]: print(id(my_tup))
4480606864 # 操作的不是原來的元組 所以可以
In [125]: my_tup = my_tup + (5,)
In [126]: print(id(my_tup))
4474234912
十六、python沒有私有方法/變量? 但是可以有”偽”的
In [132]: my_instance = my_class()
In [133]: my_instance.public_method()
Hello public world! # 普通方法
In [134]: my_instance._my_class__private_method()
Hello private world! # 私有的可以加"_ + 類名字 + 私有方法名字”
In [135]: my_instance.call_private_method_in_class()
Hello private world! # 還可以通過類提供的公有接口內(nèi)部訪問
In [136]: my_instance._my_class__private_variable
Out[136]: 1
十七、異常處理加else
In [154]: while i < 2:
.....: print(i)
.....: i += 1
.....: else:
.....: print('in else')
.....:
0
1
in else # while也支持哦~
In [155]: i = 0
In [156]: while i < 2:
.....: print(i)
.....: i += 1
.....: break
.....: else:
.....: print('completed while-loop')
.....:
0 # 被break了 沒有完全執(zhí)行完 就不執(zhí)行else里面的了
In [158]: for i in range(2):
.....: print(i)
.....: else:
.....: print('completed for-loop')
.....:
0
1
completed for-loop
In [159]: for i in range(2):
.....: print(i)
.....: break
.....: else:
.....: print('completed for-loop')
.....:
0 # 也是因?yàn)閎reak了
數(shù)據(jù)分析咨詢請掃描二維碼
若不方便掃碼,搜微信號:CDAshujufenxi
SQL Server 中 CONVERT 函數(shù)的日期轉(zhuǎn)換:從基礎(chǔ)用法到實(shí)戰(zhàn)優(yōu)化 在 SQL Server 的數(shù)據(jù)處理中,日期格式轉(zhuǎn)換是高頻需求 —— 無論 ...
2025-09-18MySQL 大表拆分與關(guān)聯(lián)查詢效率:打破 “拆分必慢” 的認(rèn)知誤區(qū) 在 MySQL 數(shù)據(jù)庫管理中,“大表” 始終是性能優(yōu)化繞不開的話題。 ...
2025-09-18CDA 數(shù)據(jù)分析師:表結(jié)構(gòu)數(shù)據(jù) “獲取 - 加工 - 使用” 全流程的賦能者 表結(jié)構(gòu)數(shù)據(jù)(如數(shù)據(jù)庫表、Excel 表、CSV 文件)是企業(yè)數(shù)字 ...
2025-09-18DSGE 模型中的 Et:理性預(yù)期算子的內(nèi)涵、作用與應(yīng)用解析 動態(tài)隨機(jī)一般均衡(Dynamic Stochastic General Equilibrium, DSGE)模 ...
2025-09-17Python 提取 TIF 中地名的完整指南 一、先明確:TIF 中的地名有哪兩種存在形式? 在開始提取前,需先判斷 TIF 文件的類型 —— ...
2025-09-17CDA 數(shù)據(jù)分析師:解鎖表結(jié)構(gòu)數(shù)據(jù)特征價(jià)值的專業(yè)核心 表結(jié)構(gòu)數(shù)據(jù)(以 “行 - 列” 規(guī)范存儲的結(jié)構(gòu)化數(shù)據(jù),如數(shù)據(jù)庫表、Excel 表、 ...
2025-09-17Excel 導(dǎo)入數(shù)據(jù)含缺失值?詳解 dropna 函數(shù)的功能與實(shí)戰(zhàn)應(yīng)用 在用 Python(如 pandas 庫)處理 Excel 數(shù)據(jù)時(shí),“缺失值” 是高頻 ...
2025-09-16深入解析卡方檢驗(yàn)與 t 檢驗(yàn):差異、適用場景與實(shí)踐應(yīng)用 在數(shù)據(jù)分析與統(tǒng)計(jì)學(xué)領(lǐng)域,假設(shè)檢驗(yàn)是驗(yàn)證研究假設(shè)、判斷數(shù)據(jù)差異是否 “ ...
2025-09-16CDA 數(shù)據(jù)分析師:掌控表格結(jié)構(gòu)數(shù)據(jù)全功能周期的專業(yè)操盤手 表格結(jié)構(gòu)數(shù)據(jù)(以 “行 - 列” 存儲的結(jié)構(gòu)化數(shù)據(jù),如 Excel 表、數(shù)據(jù) ...
2025-09-16MySQL 執(zhí)行計(jì)劃中 rows 數(shù)量的準(zhǔn)確性解析:原理、影響因素與優(yōu)化 在 MySQL SQL 調(diào)優(yōu)中,EXPLAIN執(zhí)行計(jì)劃是核心工具,而其中的row ...
2025-09-15解析 Python 中 Response 對象的 text 與 content:區(qū)別、場景與實(shí)踐指南 在 Python 進(jìn)行 HTTP 網(wǎng)絡(luò)請求開發(fā)時(shí)(如使用requests ...
2025-09-15CDA 數(shù)據(jù)分析師:激活表格結(jié)構(gòu)數(shù)據(jù)價(jià)值的核心操盤手 表格結(jié)構(gòu)數(shù)據(jù)(如 Excel 表格、數(shù)據(jù)庫表)是企業(yè)最基礎(chǔ)、最核心的數(shù)據(jù)形態(tài) ...
2025-09-15Python HTTP 請求工具對比:urllib.request 與 requests 的核心差異與選擇指南 在 Python 處理 HTTP 請求(如接口調(diào)用、數(shù)據(jù)爬取 ...
2025-09-12解決 pd.read_csv 讀取長浮點(diǎn)數(shù)據(jù)的科學(xué)計(jì)數(shù)法問題 為幫助 Python 數(shù)據(jù)從業(yè)者解決pd.read_csv讀取長浮點(diǎn)數(shù)據(jù)時(shí)的科學(xué)計(jì)數(shù)法問題 ...
2025-09-12CDA 數(shù)據(jù)分析師:業(yè)務(wù)數(shù)據(jù)分析步驟的落地者與價(jià)值優(yōu)化者 業(yè)務(wù)數(shù)據(jù)分析是企業(yè)解決日常運(yùn)營問題、提升執(zhí)行效率的核心手段,其價(jià)值 ...
2025-09-12用 SQL 驗(yàn)證業(yè)務(wù)邏輯:從規(guī)則拆解到數(shù)據(jù)把關(guān)的實(shí)戰(zhàn)指南 在業(yè)務(wù)系統(tǒng)落地過程中,“業(yè)務(wù)邏輯” 是連接 “需求設(shè)計(jì)” 與 “用戶體驗(yàn) ...
2025-09-11塔吉特百貨孕婦營銷案例:數(shù)據(jù)驅(qū)動下的精準(zhǔn)零售革命與啟示 在零售行業(yè) “流量紅利見頂” 的當(dāng)下,精準(zhǔn)營銷成為企業(yè)突圍的核心方 ...
2025-09-11CDA 數(shù)據(jù)分析師與戰(zhàn)略 / 業(yè)務(wù)數(shù)據(jù)分析:概念辨析與協(xié)同價(jià)值 在數(shù)據(jù)驅(qū)動決策的體系中,“戰(zhàn)略數(shù)據(jù)分析”“業(yè)務(wù)數(shù)據(jù)分析” 是企業(yè) ...
2025-09-11Excel 數(shù)據(jù)聚類分析:從操作實(shí)踐到業(yè)務(wù)價(jià)值挖掘 在數(shù)據(jù)分析場景中,聚類分析作為 “無監(jiān)督分組” 的核心工具,能從雜亂數(shù)據(jù)中挖 ...
2025-09-10統(tǒng)計(jì)模型的核心目的:從數(shù)據(jù)解讀到?jīng)Q策支撐的價(jià)值導(dǎo)向 統(tǒng)計(jì)模型作為數(shù)據(jù)分析的核心工具,并非簡單的 “公式堆砌”,而是圍繞特定 ...
2025-09-10