python數據類型bytes 和 bytearray的使用與區別

bytes 和 bytearray

bytes 和 bytearray 都是二進制世界的成員,用二進制的方式去理解才能看清他的本質。

理解bytes 和 bytearray

0 和 1 是計算機工作的根本,單個的0和1隻能表達兩種狀態,無法滿足我們復雜的計算,於是計算機使用瞭8位即一個byte作為一個儲存的基本單位。

byte 由 8bit 組成,例如   0000 0001  , 也可以表示為16進制的形式:0x01, 0x為固定前綴,表示該數使用16進制表示方式,此外0o前綴為8進制,0b為二進制形式,以此區分。bytes 從字面上理解是byte 的復數, 也就是多個byte組成的序列。這一點與字符串與字符的關系類似。

於是,我們可以這樣理解,字符串是由一個個字符順序儲存組成的序列,其中每個元素為一個字符。bytes 是由一個個byte組成的序列,每一個元素是一個byte。

bytearray是一個由byte為元素組成的array,其中每一個元素為一個byte。在python官方文檔中,作者簡單的定義瞭這兩個類型。翻譯為

bytes:可以看作是一組二進制數值(0-255) 的 str 序列bytearray :可以看作是一組二進制數值(0-255) 的 list 序列

python中值的表示

在計算機中表示數有多種表示方式,普通的表示方式就是10進制的數值表示方法,例如a=10,此外還有8進制,16進制,2進制的表示方式,分別使用前綴0o和0x和0b表示。

a = 97
a = 0b01100001
a = 0x61
a = 0o301

使用上面4種方式定義的值均為十進制數97,也就是上面四種方式是等價得,隻是使用不同的進制表示同一個值。

除瞭使用數值可以有不同的表示方式外,還可以使用字節的方式來定義一個值,而字節該如何書寫?python使用瞭一個特殊字符串來表示字節,這個特殊字符可以使用前綴\x,\o, \b和\ 表示,分別表示16,8,2,10進制的表示方式,特殊字符後是對應的進制表示值。示例

a = "a"
a = "\b01100001"
a = "\x61"
a = "\o301"

同樣得,這樣得四種方式是等價得,a變量的值均為"a"。以上是單字節的字符的表示,多字節字符或者是一個字符串同樣可以如此定義。

#  使用encode查看一下中國的二進制編碼為

str.encode("中國")     #  ==>   b'\xe4\xb8\xad\xe5\x9b\xbd',前綴 b表示該值的類型為bytes,區別於字符串的表示

#  因此,我們可以使用這個字節表示來定義“中國”

a = '\xe4b8ade59bbd'    # 或者 '\xe4\xb8\xad\xe5\x9b\xbd',兩種方式均可

# 其8進制可以如此計算
num = int.from_bytes(a, "big")    # 將這段字符串轉化為內存等值的數值
print(oct(num))                   # 打印該數值的8進制表示,結果為字符串類型。在該字符串加上\o前綴即可

# 最後打印結果
print(a)    # ==> "中國"

# 再假如我們需要定義一個字符串abc,可以使用下面的方式

a = "abc"

a = "\x616263"        # 由於abc的ascii碼分別為 0x61 62 63
a = "\o301302303"       # 由於abc的ascii碼分別為 0o301 302 303
a = "\979899"          # 由於abc的ascii碼分別為 979899

進制轉換

字節,數值,字符等類型可以使用下面的方式進行轉化。還包括值得不同進制表示,字符串得不同進制表示,示例:

# 內置函數
chr(97)      #    ==> "a"
ord("a")     #    ==> 97

# 將一個10進制數,轉化為其他進制的數的字符串表示得內置函數,註意,結果都是字符串,而不是對應進制的數值
bin(97)                                 # ==> '0x61'
oct(97)                                 # 8進制字符 '0o301'
hex(97)                                # 2進制字符 '0b01100001'

# 也可使用format完成
#帶前綴 0x 
format(97,"#x")                                   # ==> '0x61'
format(97,"#o")                                   # 8進制字符 '0o301'
format(97,"#b")                                   # 2進制字符 '0b01100001'
#不帶前綴
format(97,"X")                                    # ==> '61'
format(97,"o")                                    # ==> '301'
format(97,"b")                                    # ==> '01100001'

# 3.6+版本使用方法
# f'{255:X}' 和 f'{255:#X}'                   ===> " FF "  和 "0xFF"

# 使用c 風格得表示
"%x"%10                                     # ==> 'a'
"%o"%10                                     # ==> '12'
# 帶前綴                               
"%#x"%10                                     # ==> '0xa'
"%#o"%10                                     # ==> '0o12'

將一個字節或者字符或者字符串轉化為值類型可以使用如下的方式。

ord("a")          # ==> 97
int.from_bytes(b"a", "big")    # 97 類型為int,指定大端模式(小地址存多字節高位)

# 以下兩種形式等價
int.from_bytes(b"abc", "big")              #   ==> 得到三字節長度的數值 
int.from_bytes(bytes("abc"), "big")      #

bytes類型

將一個str 轉化為 bytes 的本質是將str中的每個字符轉化為該字符的二進制編碼形式。例如 a 的二進制為 0x65。

# 將字符串abc 中每一個字符轉化為二進制編碼形式就是bytes類型
s = "abc"
bs1 = bytes("abc")         # bytes()第一個參數為一個可迭代對象,將每一個元素轉為二進制的表示方式

print(s)          # 'abc'
print(bs1)       # b'abc'     python檢測到這個二進制序列可以使用字符abc表示,但是為瞭區別於abc的字符串,所以使用 b'abc',隻是給人看的一種表示結果。實際的值應該是一個個字節。

# 我們看一下單個元素
print(s[0])        #  'a'
print(bs1[0])     #  97  而不是 b'a',對於單個字節0x61, python使用瞭十進制數97表示

二進制類型bytes使用b'abc'的表示方式,隻是嘗試將這個序列中的元素翻譯為人類可以看懂的形式,實際在變量內存中儲存的為一個個二進制字節0x61 0x62 0x63,這樣的三個字節可以根據數據類型的不同翻譯成不同的結果。

b2 = bytes( [97,98,99] )
print(b2)     #  ==> b'abc'

這就對上面結論最佳證明,出現該結果的原因是,python中的列表 [97, 98, 99] 在內存中二進制儲存和字符串abc儲存的值是相同的,所以使用python來展示這段內存時,它使用b'abc'的方式進行展示。

bytes類型轉化

字符串轉bytes類型

# 將返回 bytes 類型 b" abc ", 可以添加encoding參數指定字符串的編碼方式
bs1 = bytes("abc","utf-8")
# 可以使用字符的16進制字符表達形式
bs2 = bytes('\x61\x62\x63',"utf-8")
# 直接對字符進行編碼成二進制形式
bs2 = "abc".encode()

# 16進制字符轉為bytes類型
b1 = bytes.fromhex("61 62 63")  #  ==>   b"abc"   "61,62"是兩個16進制數組合,單個值不能超過 7F也就是不能超過127 ,否則無法對應ASCII表中的字符
b1.hex()                 # ===>  '616263'   ASCII碼中abc字符對應的16進制數組成的字符串,上面函數的逆運算。

數值轉化為bytes類型

想要使用數值來定義一個不能直接使用 bytes(97) 的方式來定義,這不會在內存中儲存一個二進制的 97,而是會開辟97個字節,每個字節的值為0x00。這是需要註意的一點,如果想要儲存一個97,可以使用列表來實現。

# 傳入數值類型可迭代對象
b1 = bytes(range(97,100))               #  ==> b'abc'
b2 = bytes([97,98,99])                   #  ==> b'abc'
b3 = bytes([97])                             #  ==> b'a'

# 直接傳入10進制數值對象而不是可迭代對象,將會生成對應數值字節的空bytes
b4 = bytes(3)         #  b'\x00\x00\x00'   三個空字符的 bytes

# 通過數值轉化將8進制,16進制數字 生成bytes對象
b5 = bytes([int("61",16)])    #16進制61  == > 10 進制 ==> bytes ==> b"a" (使用字符a表示)
b6 = bytes([int("61", 8)])     # 8進制61  == > 10 進制 ==> bytes  ==> b"1" (使用字符1表示)

#  也可利用bytes 對象轉化為 10 進制 數值
num = int.from_bytes(b"abc","big")         # "abc"對應的三個字節拼接在一起作為一個二進制數,並計算為10進制數輸出
print(num)                   #    ===>   6382179

以上是將數值和字符串轉化為  字節類型,這是常用的類型轉化方式,同樣的,也可以將字節轉化為字符或者值的類型。使用以下的方法即可

bytes轉數值

bytes類型轉化為 值類型使用 數值使用 int的一個類方法from_bytes即可,示例:

num = int.from_bytes(b“abc”, "big")    # 將這個bytes對象轉化為內存等值的數值

bytes的方法

bytes對象的方法和字符串的方法基本一致,可以進行字符串的幾乎所有操作,例如字符串的replace,split,partation等操作,唯一需要區別的是。字符串的操作操作的對象是一個字符串或者單個字符,而bytes對象操作的是一個bytes二進制字節,例如進行字符串替換時:

"abc".replace("a", "x")     # 將字符a 替換為 x

# 而在bytes對象

b"abc".replace(b"a", b"x")     # 無論是參數中的對象還是對象本身都是bytes類型,有b“”前綴標識

其他方法類比於str的方法,並結合官方文檔即可。

bytearray

bytearray類似於中的list數據類型,bytearray中的每一個元素始終為一個字節值,也就是 0x00 – 0xff 區間的一個值,如果不在該范圍內的值,在指定瞭編碼方式的情況下可以按照字節遍歷

初始化一個bytearray對象。同樣需要一個可迭代對象作為參數,將會按照字節遍歷整個可迭代對象,有以下的方式

bytearray("abc", encoding="utf-8")      # abc字符串使用utf-8的方式編碼為字節,每個字節作為bytearray對象的一個元素儲存即可。
bytearray(b"abc")                       # 也可以使用bytes 類型,而使用bytes類型就沒有編碼一說瞭
bytearray([97, 98, 99])                 # 使用可迭代對象,內部元素為一個一個0-255的數值類型。

ba1 = bytearray(range(97,103))          # 可迭代當然包括range對象
ba1                                     #  bytearay對象,==>  bytearray(b"abcdef")
ba1[0]                                  #  ==>  97  (integer)
ba1[1:4]                                #  切片 ==> bytearray(b'bcd')

# 賦值,可變 bytearray
ba[ 4 ] = 122                           #  122整型對應字符"z",   ==> b"e" --> b"z"
ba                                    #   bytearray(b"abcdzf" )
ba1[1:4] = b"xyz"                     # 切片賦值,替換ba1[1:4]的內容, 隻有bytes 或bytearray 序列可賦值

bytes和bytearray類型之間可以直接進行轉化,bytes()中可以傳入一個bytearray對象作為參數,並且不存在編碼問題,因為兩個類型都是一個二進制的序列。

python在展示bytearray對象,使用的是bytesarray(b"abc") 的方式,其實,不妨理解為 bytearray( [ b"a",  b"b",  b"c" ] ) 的形式。也就是每個元素為字節列表。所以byteaarray實現瞭和列表對象的方法,並且可以進行元素的替換,也就是與列表相同,他是一個可變類型的對象。而bytes 與字符串都是不可變的,無法對單個元素進行替換,對象一旦產生將不可變。如果需要改變,需要新建一個新的對象。

bytearray的方法

類似與 列表對象,可以進行切片,append, extend,insert等操作,同樣的,由於內部的元素都是一個個字節對象,對單個元素操作時隻能操作單字節的對象,而執行extend這類處理可迭代對象方法,也要求可迭代對象內部的所有元素為單字節對象。單字節對象,可以是 b"a"類似的單字節對象  或  0-255的數值。因為一個字節隻能儲存256中狀態。以下是幾個簡單的示例

a = bytearray(b"abc")

a.append(100)
print(a)             # bytearray(b"abcd")

a.append(b"e")
print(a)            # bytearray(b"abcde")

a.extend(a)       
print(a)            # bytearray(b"abcdeabcde")

a.insert(0, b"A")
print(a)            # bytearray(b"Aabcdeabcde")

bytes(a)           # b"Aabcdeabcde"

 到此這篇關於python數據類型bytes 和 bytearray的使用與區別的文章就介紹到這瞭,更多相關python bytes 和 bytearray內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: