Python 二十三大實踐、編碼建議和技巧
- 2020 年 2 月 19 日
- 筆記
一、開篇
2020年,你又立了什麼新的 Flag?新一年,我為大家準備 23 個非常優秀的 Python 實踐技巧。希望這些訣竅能在實際工作中幫助大家,並且學到一些有用的知識。
二、技巧篇
1、檢查並使用滿足需求的最小Python版本
你可以在程式碼中檢查Python 版本,以確保你的程式碼使用者沒有使用不兼容的版本運行腳本。使用以下程式碼進行簡單的檢查:
import sys if not sys.version_info > (2, 7): print('當前Python版本低於2.7') elif (2, 7) <= sys.version_info <= (3, 8): print('當前Python版本大於或等於2.7但小於3.8') else: print('其它版本')
2、列表解析式
列表解析式可以用來替換通過循環來填充列表的醜陋方法,其基本語法是:
[ expression for item in list if conditional ]
一個非常基礎的例子,用於生成包含連續數字的列表:
mylist = [i for i in range(10)] print(mylist) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
由於可以使用表達式,因此可以通過更複雜的數學方法來生成列表:
squares = [x**2 for x in range(10)] print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
甚至也可以調用外部函數:
def some_function(a): return (a + 5) / 2 my_function = [some_function(i) for i in range(10)] print(my_function) # [2, 3, 3, 4, 4, 5, 5, 6, 6, 7]
最後,也可以用if作為生成條件來對列表進行過濾。在下面的例子中,只有偶數被保留:
filtered = [i for i in range(20) if i%2==0] print(filtered) # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
3、檢查對象的記憶體佔用情況
通過sys.getsizeof(object)命令可以查看任何對象的記憶體使用情況:
import sys mylist=range(0, 1000000) print(sys.getsizeof(mylist))
因為range函數返回的是一個類對象,這個類對象表現為一個列表。因此使用range函數比使用實際的包含一萬個數字的列表要更加節省記憶體。
4、'==' VS 'is'用法
等於(==)和 is 是 Python 中對象比較常用的兩種方式。簡單來說,'=='操作符比較對象之間的值是否相等,比如下面的例子,表示比較變數 a 和 b 所指向的值是否相等。
a == b
而'is'操作符比較的是對象的身份標識是否相等,即它們是否是同一個對象,是否指向同一個記憶體地址。在 Python 中,每個對象的身份標識,都能通過函數 id(object) 獲得。因此,'is'操作符,相當於比較對象之間的 ID 是否相等。
a = 10 b = 10 a == b True id(a) 4427562448 id(b) 4427562448 a is b True
不過,需要注意,對於整型數字來說,以上a is b為 True 的結論,只適用於 -5 到 256 範圍內的數字。比如下面這個例子:
a = 257 b = 257 a == b True id(a) 4473417552 id(b) 4473417584 a is b False
這裡我們把 257 同時賦值給了 a 和 b,可以看到a == b仍然返回 True,因為 a 和 b 指向的值相等。但奇怪的是,a is b返回了 false,並且我們發現,a 和 b 的 ID 不一樣了,這是為什麼呢?
這是由於Python出於對性能優化的考慮,Python 內部會對 -5 到 256 的整型維持一個數組,起到一個快取的作用。這樣,每次你試圖創建一個 -5 到 256 範圍內的整型數字時,Python 都會從這個數組中返回相對應的引用,而不是重新開闢一塊新的記憶體空間。但是,如果整型數字超過了這個範圍,比如上述例子中的 257,Python 則會為兩個 257 開闢兩塊記憶體區域,因此 a 和 b 的 ID 不一樣,a is b就會返回 False 了。
通常來說,在實際工作中,當我們比較變數時,使用'=='的次數會比'is'多得多,因為我們一般更關心兩個變數的值,而不是它們內部的存儲地址。但是,當我們比較一個變數與一個單例(singleton)時,通常會使用'is'。
5、返回多個值
Pyhon中的函數都可以返回多個變數,而不需要字典,列表或者類作為返回對象。方法如下:
def get_user(id): # fetch user from database # .... return name, birthdate name, birthdate = get_user(4)
對於有限數量的返回值,這是可以的。但是任何超過3個值的內容都應該放到一個(data)類中。
6、使用 data 類
從3.7版本開始,Python提供了 data 類。與常規類或其他替代方法(如返回多個值或字典)相比,有以下幾個優點:
- 數據類需要至少一定數量的程式碼
- 可以通過 __eq__ 方法來比較不同的data類對象
- 可以 __repr__ 通過很容易地列印一個數據類來進行調試
- 數據類需要類型提示,因此減少了 bug
一個data類的例子如下:
from dataclasses import dataclass @dataclass class Card: name: str sex: str card = Card("MikeZhou", "Man") print(card.name) # 'Mikezhou' print(card.sex) # 'Man' print(card)
詳細教程參見:
https://realpython.com/python-data-classes/
7、字典合併(Python 3.5+)
從Python 3.5開始,字典的合併變得更簡單了:
dict1 = { 'a': 1, 'b': 2 } dict2 = { 'b': 3, 'c': 4 } merged = { **dict1, **dict2 } print (merged) # {'a': 1, 'b': 3, 'c': 4}
8、將字元串轉化為標題格式
在標題格式中,非介詞的首字母會大寫。可以通過.title()方法實現:
mystring = "awesome python tricks" print(mystring.title()) 'Awesome Python Tricks'
9、將列表中的字元串合併到一起
從列表中創建字元串,並在兩個單詞間插入空格:
mylist = ['mikezhou_talk', '中文名是:', '測試開發技術'] mystring = " ".join(mylist) print(mystring) # 'mikezhou_talk 中文名是: 測試開發技術'
也許你會疑惑,為什麼不使用mylist.join(" ")呢?歸根結底,String.join()函數不僅可以連接列表,還可以連接任何可迭代的列表。將它放在String中會阻止在多個位置實現相同的功能。
10、Emoji表情

這些表情具有很強的表達能力,能給人留下深刻印象。更重要的是,這在分析社交媒體數據時尤其有用。
首先通過以下命令安裝emoji模組:
pip3 install emoji
可以按照以下方法使用表情:
import emoji result = emoji.emojize('Python is :thumbs_up:') print(result) # 'Python is ?' # You can also reverse this: result = emoji.demojize('Python is ?') print(result) # 'Python is :thumbs_up:'
更多複雜的例子以及文檔,參見:
https://pypi.org/project/emoji/
11、翻轉字元串和列表
可以用切片操作來翻轉列表或字元串,將step設置為負值即可實現:
revstring = "abcdefg"[::-1] print(revstring) # 'gfedcba' revarray = [1, 2, 3, 4, 5][::-1] print(revarray) # [5, 4, 3, 2, 1]
12、圖片顯示
可以通過Pillow模組來顯示圖片,首先安裝python圖片庫:
pip3 install Pillow
然後下載你要顯示的圖片,並重命名。然後可以通過以下命令來顯示圖片:
from PIL import Image im = Image.open("mikezhou.jpg") im.show() print(im.format, im.size, im.mode) # JPEG (1920, 1357) RGB
Pillow的功能遠不止顯示圖片。它可以對圖片進行分析,調整大小,濾波,增強,變形等等。更多資料詳見文檔:
https://pillow.readthedocs.io/en/stable/。
13、從列表或字元串中獲取唯一元素
通過set()函數可以將列表或字元串轉換為集合,集合中的不含重複元素:
mylist = [1, 1, 2, 3, 4, 5, 5, 5, 6, 6] print (set(mylist)) # {1, 2, 3, 4, 5, 6} print (set("aaabbbcccdddeeefff")) # {'a', 'b', 'c', 'd', 'e', 'f'}
14、找出最常出現的值
查找列表或字元串中最常出現的值:
test = [1, 2, 3, 4, 2, 2, 3, 1, 4, 4, 4] print(max(set(test), key = test.count)) # 4
你可以嘗試自行理解上述程式碼。好吧,也許你並沒有嘗試。上述程式碼的工作原理如下:
- max()將返回列表中的最大值。key參數接受單個參數函數確定訂製排序順序,在本例中,它是test.count,該函數應用於iterable對象中的每個元素。
- .count()是列表的一個內建函數,該函數接收一個參數,並計算該參數的出現次數。因此在本例中,test.count(1)返回2,testcount(4)返回4。
- set(test)返回test列表中的所有唯一值,因此是{1,2,3,4}。
因此在上面的這行語句中我們首先找出了test列表的所有獨特值,即{1,2,3,4}。接著,將.count函數應用於set中的每個值,得到一個數量列表,然後通過max找出數量最大的值。
15、創建進度條
可以自行創建進度條,但也可以通過progress模組來快速創建:
pip3 install progress
然後通過以下程式碼來創建進度條:
from progress.bar import Bar bar = Bar('Processing', max=20) for i in range(20): # Do some work bar.next() bar.finish()
16、快速創建web伺服器
您可以快速啟動web伺服器,來提供當前工作目錄的內容:
python3 -m http.server
如果您想與同事共享一些內容,或者想測試一個簡單的HTML站點,這是非常有用的。
17、用於條件賦值的三元運算符
這是另一種使你程式碼變得簡潔,同時保持可讀性的方法:
[on_true] if [expression] else [on_false]
一個簡單的例子如下:
x = "Success!" if (y == 2) else "Failed!"
18、統計元素的出現次數
可以使用Collections依賴包中的Counter方法來獲得一個包含列表中所有惟一元素計數的字典:
from collections import Counter mylist = [1, 1, 2, 3, 4, 5, 5, 5, 6, 6] c = Counter(mylist) print(c) # Counter({1: 2, 2: 1, 3: 1, 4: 1, 5: 3, 6: 2}) print(Counter("aaaaabbbbbccccc")) # Counter({'a': 5, 'b': 5, 'c': 5})
19、加入色彩

通過 Colorama 依賴包,可以在終端中添加更多色彩:
from colorama import Fore, Back, Style print(Fore.RED + 'some red text') print(Back.GREEN + 'and with a green background') print(Style.DIM + 'and in dim text') print(Style.RESET_ALL) print('back to normal now')
關於Colorama依賴包的更多資訊,參見:
https://pypi.org/project/colorama/
20、日期處理
python-dateutil模組為標準的datetime模組提供了強大的擴展。首先安裝該模組:
pip3 install python-dateutil
你可以用這個庫做很多很酷的事情。我講把我認為特別有用的一個功能作為示例:日誌文件中日期的模糊解析等。如下:
from dateutil.parser import parse logline = 'INFO 2020-01-01T00:00:01 Happy new year, human.' timestamp = parse(log_line, fuzzy=True) print(timestamp) # 2020-01-01 00:00:01
只要記住,如果datatime不具備某個功能,那datautil一定有該功能,datautil是datatime功能的延續。
21、通過chardet檢測字符集合
可以使用chardet模組來檢測文件中的字符集合。這在分析大量隨機文本時非常有用。安裝chardet模組:
pip install chardet
現在你有了一個額外的命令行工具chardetect,它可以這樣使用:
chardetect somefile.txt somefile.txt: ascii with confidence 1.0
你也可以通過編程的方式來使用這個依賴包,詳見文檔:
https://chardet.readthedocs.io/en/latest/usage.html
22、用 cProfile 進行性能分析
日常工作中,我們常常會遇到這樣的問題:在線上,產品的某個功能模組效率低下,延遲(latency)高,佔用的資源多,但卻不知道是哪裡出了問題。
如果能對程式碼的每個部分進行動態的分析,比如準確計算出每個模組消耗的時間等。這樣就可以知道程式的瓶頸所在,從而對其進行修正或優化。在 Python 中,這些需求用 cProfile 就可以實現。
舉個例子,比如我想計算斐波拉契數列,運用遞歸思想,我們很容易就能寫出下面這樣的程式碼:
def fib(n): if n == 0: return 0 elif n == 1: return 1 else: return fib(n-1) + fib(n-2) def fib_seq(n): res = [] if n > 0: res.extend(fib_seq(n-1)) res.append(fib(n)) return res fib_seq(30)
接下來,我想要測試一下這段程式碼總的效率以及各個部分的效率。那麼,我就只需在開頭導入 cProfile 這個模組,並且在最後運行 cProfile.run() 就可以了:
import cProfile # def fib(n) # def fib_seq(n): cProfile.run('fib_seq(30)')
或者更簡單一些,直接在運行腳本的命令中,加入選項「-m cProfile」也很方便:
python3 -m cProfile xxx.py
運行完畢後,我們可以看到下面這個輸出介面:

23、合理利用assert
Python 的 assert 語句,可以說是一個 debug 的好工具,主要用於測試一個條件是否滿足。如果測試的條件滿足,則什麼也不做,相當於執行了 pass 語句;如果測試條件不滿足,便會拋出異常 AssertionError,並返回具體的錯誤資訊。
比如下面這個例子:
assert 1 == 2, 'assertion is wrong'
它相當於下面這兩行程式碼:
if __debug__: if not expression1: raise AssertionError(expression2)
這裡的__debug__是一個常數。如果 Python 程式執行時附帶了-O這個選項,比如Python test.py -O,那麼程式中所有的 assert 語句都會失效,常數__debug__便為 False;反之__debug__則為 True。
再來看一個例子:
def apply_discount(price, discount): updated_price = price * (1 - discount) assert 0 <= updated_price <= price, 'price should be greater or equal to 0 and less or equal to original price' return updated_price
我們加入了 assert 語句,規定銷售數目必須大於 0,這樣就可以防止後台計算那些還未開賣的課程的價格。
總的來說,assert 在程式中的作用,是對程式碼做一些 internal 的 self-check。使用 assert,就表示你很確定。這個條件一定會發生或者一定不會發生。如果你的程式沒有 bug,那麼 assert 永遠不會拋出異常;而它一旦拋出了異常,你就知道程式存在問題了,並且可以根據錯誤資訊,很容易定位出錯誤的源頭。
以上就是為大家整理的23個Python常用技巧,希望這些技巧能幫助你在新的一年裡有個不錯的開始。
無論是對於 Python 這門語言,還是其他語言,或是電腦的其他領域,我認為實踐永遠是至關重要的。電腦科學是一門偏向工程的學科,所以一定要多實踐,多寫程式碼,多交流,多思考。
希望這篇文章能幫到你!更多乾貨文章請關注我們。