免費視頻教程!零基礎學Python系列(7) - 數據類型之bytes(上)

本節我們開始講python數據類型之bytes類型,我們分爲上下兩個章節。

你可以直接到這個頁面觀看本節視頻:免費視頻教程!零基礎學Python系列(7) - 數據類型之bytes(上)

以下爲對應的課件內容:


Bytes是python3新增的一個數據類型,用於表示一個字節串,它是一個有序的序列。

通常有兩種方式來構造一個bytes類型的對象:

1、通過bytes()函數構造

bytes_1 = bytes('hello', 'utf-8')
bytes_2 = bytes([1, 200, 80, 50])

2、通過b後面跟字符串的方式

bytes_3 = b'world'
bytes_4 = b'\x77\x6f\x72\x6c\x64'

我們在print一個bytes類型數據時,python會以/x的格式依次打印每個字節的值,以兩位16進制來顯示。但是python對於一些字符會直接字符編碼轉換,所以造成打印出來的結果看起來很混亂,比如:

​​​​​​​bytes_2 = bytes([1, 200, 80, 50])

print('bytes_2:', bytes_2)

輸出結果爲:

bytes_2: b'\x01\xc8P2'

最後兩個數值80、50,被轉換爲了字符P、2,看起來很混亂。

 

這時,我們可以寫一個簡單的方法,讓它不做這種轉換:

# bytes 按照16進制輸出,強制不ascii轉碼
def trans(s):
    return "b'%s'" % ''.join('\\x%.2x' % x for x in s)

bytes_2 = bytes([1, 200, 80, 50])

print('bytes_2:', trans(bytes_2))

輸出結果爲:

bytes_2: b'\x01\xc8\x50\x32'

這樣我們看到,bytes裏面包含了一個一個的字節。

因爲我們還沒有學函數的概念,所以大家只要知道在輸出的時候調用這個方法即可。

 

bytes類型,存儲的是一系列的字節,它並不關注這些字節具體表示什麼含義(字符、網絡數據、圖片、音視頻等)。Bytes並不約束你如果使用這些字節數據,你可以按照你自己的功能邏輯做任意的轉換。這個轉換邏輯,不是bytes數據類型的功能範疇。

 

比如:對於字符,通常我們需要對其做一個編碼轉換,將字節類型轉換爲有意義的字符串。這個轉換規則,就是字符編碼,緊接着下一小節我們會介紹字符編碼。

 

我們可以看到,bytes類型也是一種序列,所以它的大多數操作方法和String一致。

# 操作方法
print(bytes_3[0: 3])
print(bytes_1 + bytes_3)
print(b'h' in bytes_1)
print(bytes_1.split(b'l'))
print(bytes_1.find(b'll'))
print(bytes_1.replace(b'l', b't'))

輸出結果爲:

b'wor'

b'helloworld'

True

[b'he', b'', b'o']

2

b'hetto'

是不是和string類型高度一致? bytes類型和string類型的對比如下:

  • string的基本單位是字符,bytes的基本單位是字節;
  • 他們都是屬於一種序列,所以對於序列的操作方法,對他們基本都適用;
  • String和bytes都是不可變類型,不能對其元素進行修改。

 

注意,雖然bytes通常會和string一起使用,但是bytes並不只是給string用,它本質上是一個字節串。Bytes適合那種面向二進制流的存儲數據,比如圖片、視頻等多媒體,或者網絡通信等二進制報文流。

 

  • 字節序

字節序,顧名思義就是字節存儲的順序。大家可能覺得奇怪,字節不都是“從左到右”依次存儲的嗎?怎麼會有字節序的問題?大家看看下面的例子:

#  author: Tiger,    wx ID:tiger-python

# file: ./6/6_2.py

# bytes 按照16進制輸出,強制不ascii轉碼
def trans(s):
    return "b'%s'" % ''.join('\\x%.2x' % x for x in s)


# 字節序
byte_1 = 'python'.encode('utf-8')
print(trans(byte_1))

print('Big endian: ', hex(int.from_bytes(byte_1, byteorder='big', signed=False)))
print('Little endian: ', hex(int.from_bytes(byte_1, byteorder='little', signed=False)))

輸出結果爲:

b'\x70\x79\x74\x68\x6f\x6e'

Big endian:  0x707974686f6e

Little endian:  0x6e6f68747970

上面的實例中,我們將bytes類型b’python’強制轉換爲int類型,在轉換過程中分別指定其字節序爲big和little。從打印結果可以看出,這兩種類型對應的輸出結果完全相反。它們對應的就是大端字節序(Big endian,BE)小端字節序(Little endian,LE)

比如我要存儲一個字節串:b’\x12\x34\x56\x78’:

大端字節序:從低地址到高地址,依次存儲數據字節;

小端字節序:相反,從高地址到低地址,依次存儲數據字節。因爲我們查看內存通常是由低位地址向高位地址看,所以大端字節序是更加符合我們的習慣的,而小端則相反。

爲什麼計算機會產生兩種不同的字節序呢?

因爲字節序是由CPU架構決定,而在計算機技術發展初期,CPU架構的兩大陣營X86和PowerPC分別採用了完全相反的兩種字節序,X86採用了LE,PowerPC採用了BE。所以,纔會導致我們現在需要面對字節序的問題。

 

我們可以下面的方法獲取當前cpu的字節序類型:

# 獲取當前cpu的字節序類型

import sys
print('endian of cur env:', sys.byteorder)

輸出爲:

endian of cur env: little

我使用的環境是X86的CPU,對應的是小端字節序。

 

如果你的程序只會在本地運行,不會涉及到跨主機(跨不同類型CPU)的操作,那麼你不需要關注字節序。反之,你需要特別關注字節序,因爲它容易出錯。

如果計算機A採用了BE架構的CPU,計算機B採用了LE架構的CPU。我們有一段程序,在計算機A發送一個bytes : b’\x12\x34\x56\x78’給計算機B,那麼計算機B解析出來的數據將是bytes : b’\x78\x56\x34\x12’,這就完全錯了。

 

在這種跨主機的數據傳輸中的字節序,我們通常稱之爲網絡字節序,網絡字節序和CPU無關,它是網絡通信協議定義的一套規範。幾乎所有的網絡字節序都採用了大端字節序BE。計算機將數據發送給網絡協議之前,需要統一轉換爲網絡字節序,同樣,接收端的計算機從網絡接收到數據後,也會統一將其由網絡字節序轉換爲本機字節序。這樣,我們就解決了跨主機的字節序問題。Python的網絡編程裏面,我們還會涉及到字節序,到時候我們可以回頭來看看。

 

下一節我們繼續講字符編碼和string與bytes直接的轉換。


本節課程的視頻和實例源碼下載方式:點擊->我的主頁,查看個人簡介。

我儘量堅持每日更新一節。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章