【CSDN 編者按】大家一起來找 Bug。
原文鏈接:https://dwrodri.gitlab.io/can-you-spot-the-bug-in-this-python-code/
本文已獲作者授權,未經允許,禁止轉載!
作者 | Derek Rodriguez
譯者 | 彎月
責編 | 夏萌
出品 | CSDN(ID:CSDNnews)
最近,在解析文本時,我遇到了一個十分有趣的問題。在深入探討之前,讓我先來介紹一下背景故事。我的任務是分析文本文件中的一些以逗號分隔的數據,如下所示:
這個文本文件包含若干寬度可變的十六進制值,每行至少三個字段。我只關心第一個和第三字個段。在我看來,分析工作可分爲三步:
- 循環讀取每一行數據;
- 利用逗號將數據分解成一個列表;
- 選取第一個和第三個元素,並將它們轉換爲整數。
看似很簡單,我可以使用 pandas DataFrame 編寫幾行代碼就夠了。
下面是我編寫的代碼:
你發現 bug 了嗎?反正我沒看出來。下面,我來詳細解釋一下這段代碼,並深入剖析我究竟錯在哪兒了。
代碼詳解
CSV文件是列表的列表
我簡單地認爲,CSV 數據就是列表的列表。因此,我可以將各個元素視爲嵌入列表。我從網上的一篇帖子中找到了讀取嵌入列表的代碼,然後複製粘貼:
nested_lists = [[1,2,3],[4,5,6],[7,8,9]]flattened_list = [element for sublist in nested_lists for element in sublist]
以前我曾接觸過 C 和 C++,之後才學習了 Python,因此在學習嵌套推導式時,我感覺 Python 只是機器可以理解的僞代碼。這個嵌套列表會生成以下字節碼:
然後,我一些自己的代碼進行擴展,最終得到了以下代碼:
錯誤
事實證明,Python 無法按照我的想象將可迭代的文本分解與推導式結合起來,你必須把 .split(",") 調用放在另一個列表中:
這讓我有點傷腦筋,因爲 .split(",") 本身就是一個列表,將它打包到另一個列表中,豈不變成了雙重嵌套列表?我不太明白。我嘗試通過編譯器瀏覽器尋找答案。下圖展示了正確的生成器表達式與我編寫的代碼之間的差異:
你看出問題所在了嗎?代碼中的問題在於,在分解文本之前,.split() 的返回值是迭代器。我不確定,但我相信這關係到最初提出列表推導式的建議時確立的實現細節。
最後,我在 CPython 的貢獻者 Crowthebird 的幫助下解決了這個問題,他演示了在不使用推導式的情況下重寫代碼的問題。
錯誤的寫法:
正確的寫法:
這個問題可以得到解決嗎?
這實際上是因爲我對 Python解釋器的理解有錯,解釋器本身沒有問題。我不認爲按照我的理解修改語言會更好,因爲如此很難區分在嵌套的情況下容器何時應該解構,何時應該重用,此外列表推導式會返回元組,而 PEP 202 規定不允許。