首頁 > 軟體

python資料型別bytes 和 bytearray的使用與區別

2022-02-16 19:00:59

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'xe4xb8xadxe5x9bxbd',字首 b表示該值的型別為bytes,區別於字串的表示

#  因此,我們可以使用這個位元組表示來定義「中國」

a = 'xe4b8ade59bbd'    # 或者 'xe4xb8xadxe5x9bxbd',兩種方式均可

# 其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('x61x62x63',"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'x00x00x00'   三個空字元的 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內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com