天天看點

python bytes轉str_python:編寫高品質代碼一:關鍵詞二:編碼風格三:編碼四:輔助函數五:切割序列六:單次切片不同時指定 start、end 和 stride七:推導式八:不要使用含有兩個以上表達式的清單推導

本内容參考:Effective Python,有時間的同學可以購買原書籍閱讀

一:關鍵詞

  • Python:多指 python3 版本,本人使用的是 Python3.6

二:編碼風格

代碼風格:https://www.python.org/dev/peps/pep-0008/#introduction 5

注釋風格:https://www.python.org/dev/peps/pep-0257/ 1

Pylint 是 Python 源碼靜态分析工具,可檢查代碼是否符合 PEP 8 風格指南

http://www.pylint.org/ 2

可在 pycharm 中下載下傳 Pylint 插件,在左下角會辨別出 Pylint 字樣,點選浏覽即可審查代碼,Pylint 的檢查非常嚴格:

python bytes轉str_python:編寫高品質代碼一:關鍵詞二:編碼風格三:編碼四:輔助函數五:切割序列六:單次切片不同時指定 start、end 和 stride七:推導式八:不要使用含有兩個以上表達式的清單推導

image1371×923 152 KB

三:編碼

Python3 有兩種字元序列類型: str 和 bytes ,其中 bytes 的執行個體是位元組,其對應 8 位二進制資料, str 的執行個體包括 Unicode 字元,可以用 utf-8 編碼方式把 Unicode 字元轉為二進制資料,反之同理。Python3 使用 encode() 和 decode() 分别對應上述操作:

python bytes轉str_python:編寫高品質代碼一:關鍵詞二:編碼風格三:編碼四:輔助函數五:切割序列六:單次切片不同時指定 start、end 和 stride七:推導式八:不要使用含有兩個以上表達式的清單推導

在程式内部,建議使用 Unicode ,把任何外部輸入轉換為 Unicode ,而對外輸出則采用 bytes 。這樣可以保證外部編碼不影響内部使用,同時輸出穩定(都是 bytes )。以下代碼實作了 二進制 與 Unicode 格式互轉:

def to_str(bytes_or_str):    if isinstance(bytes_or_str, bytes):        value = bytes_or_str.decode('utf-8')    else:        value = bytes_or_str    return value # Instance of bytesdef to_bytes(bytes_or_str):    if isinstance(bytes_or_str, str):        value = bytes_or_str.encode('utf-8')    else:        value = bytes_or_str    return value # Instance of str
           

Python 的 open() 方法預設使用 encoding() 方法,即要求傳一個 Unicode 字元,它會幫你轉成二進制,如果你傳的是二進制資料,就會報錯

python bytes轉str_python:編寫高品質代碼一:關鍵詞二:編碼風格三:編碼四:輔助函數五:切割序列六:單次切片不同時指定 start、end 和 stride七:推導式八:不要使用含有兩個以上表達式的清單推導

參考以下代碼及輸出結果, os.urandom() 産生随機的 bytes 值,把它寫入 random.bin 檔案會報錯:

def main():    with open('./random.bin', 'w') as f:        f.write(os.urandom(15))if __name__ == '__main__':    main()
           
python bytes轉str_python:編寫高品質代碼一:關鍵詞二:編碼風格三:編碼四:輔助函數五:切割序列六:單次切片不同時指定 start、end 和 stride七:推導式八:不要使用含有兩個以上表達式的清單推導

image893×217 30.7 KB

以下是官方源碼給出的注釋:

In text mode, if encoding is not specified the encoding used is platform dependent: locale.getpreferredencoding(False) is called to get the current locale encoding.

隻需要将寫入模式改為二進制寫入即可:

```pythondef main():    with open('./random.bin', 'wb') as f:        f.write(os.urandom(15))if __name__ == '__main__':    main()
           

四:輔助函數

Python 有很多強大的特性,如果過度使用,會讓代碼晦澀難懂,考慮以下代碼及傳回結果:

from urllib.parse import parse_qsmy_values=parse_qs('red=5&blue=0&green=',                   keep_blank_values=True)print(repr(my_values))>>>{'red': ['5'], 'blue': ['0'], 'green': ['']}
           

三種顔色都有傳回值,用 get() 方法擷取内容時,會打出下面内容:

print('Red: ', my_values.get('red'))print('Green: ', my_values.get('green'))print('xxxx: ', my_values.get('xxxx'))>>>Red:  ['5']Green:  ['']xxxx:  None
           

發現一個問題,當原 list 為空時, get 方法傳回空,當原 key 不存在時(比如xxxx), get 方法傳回 None ,現在利用 Python 的特性,将上述代碼優化。 Python 中空字元串、空

清單及零值都是 False :

# 優化一print('Red: ', my_values.get('red', [''])[0] or 0)print('Green: ', my_values.get('green', [''])[0] or 0)# 當字典沒有這個值時, get 方法會傳回第二個參數值 ['']print('xxxx: ', my_values.get('xxxx', [''])[0] or 0)>>>Red:  5Green:  0xxxx:  0# 優化二read = my_values.get('red', [''])print('Red: ', read[0] if read[0] else 0)
           

無論是優化一還是優化二,都讓代碼少,但複雜晦澀。此時不如向特性做出妥協,使用傳統的 if/else 文法,把要實作的功能封裝到函數中,稱之為輔助函數:

def get_first_int(value: dict, key, default=0):    found = value.get(key, [''])    if found[0]:        found = found[0]    else:        found = default    return foundprint('Red: ', get_first_int(my_values, 'red'))
           

五:切割序列

Python 可對序列進行切割,基本寫法是 list[start:end] ,其中 start 所指元素會在切割後的範圍内, 而 end 所指元素不會被包括在切割結果中。檢視下面代碼及輸出結果:

a = ['a','b','c','d','e','f','g','h','i']print('First four:',a[:4])print('last four:',a[-4:])print('Middle three:',a[3:-3])>>> First four: ['a', 'b', 'c', 'd']last four: ['f', 'g', 'h', 'i']Middle three: ['d', 'e', 'f']
           

start 和 end 可以越界使用,是以可以限定輸入序列的最大長度,比如限定長度為 20 :

a=['a', 'v', 'c']print(a[:20])print(a[-20:])>>> ['a', 'v', 'c']['a', 'v', 'c']
           

對切割後的内容進行任何操作,都不會影響到原 list ,比如:

a=['a', 'v', 'c']b=a[1:]b[0]=10print('a: ' , a)print('b: ', b)>>> a:  ['a', 'v', 'c']b:  [10, 'c']
           

可以對 list 中的值進行擴張,把清單中指定範圍的值替換成新值,比如:

a1 = ['a', 'v', 'c', 'h']a1[0:10]=['f','f']print('a1: ', a1)a2 = ['a', 'v', 'c', 'h']a2[2:3]=['f','f']print('a2: ', a2)>>> a1:  ['f', 'f']a2:  ['a', 'v', 'f', 'f', 'h']
           

六:單次切片不同時指定 start、end 和 stride

Python 提供更激進的切片操作 somelist[start:end:stride] ,可以指定步進值 stride 實作取出奇索引和偶索引:

a = ['i','love','hogwarts','every','day']odds = a[::2]evens = a[1::2]print(odds)print(evens)>>>['i', 'hogwarts', 'day']['love', 'every']
           

甚至可以進行反轉操作:

a = ['i', 'love', 'hogwarts', 'every', 'day']b = b'abcdefgh'reverse_a = a[::-1]reverse_b = b[::-1]print(reverse_a)print(reverse_b)>>> ['day', 'every', 'hogwarts', 'love', 'i']b'hgfedcba'
           

這個技巧适合位元組串和 ASCII 字元,對于編碼成 UTF-8 位元組串的 Unicode ,會出問題:

a = '霍格沃茲測試學院'b = a.encode('utf-8')c = b[::-1]d = c.decode('utf-8')print(d)>>>Traceback (most recent call last):  File "xxx", line 5, in     d = c.decode('utf-8')UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa2 in position 0: invalid start byte
           

另外,2::2 , -2::-2 , -2:2:-2 和 2:2:-2 的意思同理,如果參數過多,意思會非常難以了解,不應該把 stride 與 start 和 end 寫在一起。盡量采用正值 stride ,省略 start 和 end 索引。如果一定要配合 start 或 end 索引來使用 stride,可以先步進式切片,把切割結果賦給變量,然後在變量上再進行切割。也可以使用 islide ,它不允許 start , end 或 stride 有負值。

a = ['i','love','hogwarts','every','day']b = a[::2]c = b[1:-1]print(c)>>>['hogwarts']
           

七:推導式

Python 可根據一份 list 來制作另一份,對 dict 也适用,參考以下代碼及執行結果:

a = ['i','love','hogwarts','every','day']b1 = [k+'ff' for k in a]b2 = [k+'ff' for k in a if k == 'every']print('b1: ',b1)print('b2: ',b2)>>>b1:  ['iff', 'loveff', 'hogwartsff', 'everyff', 'dayff']b2:  ['everyff']
           

當然, map 與 filter 也可以做到上述效果,但很難了解。字典(dict)和集合(set)也有類似的推導機制,參考以下執行結果:

a = {'a': 'i', 'b': 'love', 'c': 'hogwarts', 'd': 'every', 'e': 'day'}b1 = {key:value+'ff' for key, value in a.items()}b2 = {key:value+'ff' for key, value in a.items() if key == 'd' or key == 'a'}print('b1: ', b1)print('b2: ', b2)>>>b1:  {'a': 'iff', 'b': 'loveff', 'c': 'hogwartsff', 'd': 'everyff', 'e': 'dayff'}b2:  {'a': 'iff', 'd': 'everyff'}
           

八:不要使用含有兩個以上表達式的清單推導

todo

(文章來源于霍格沃茲測試學院)