Pythonのbytes入門:エンコード・デコード・bytearrayまで解説

python logo notext.svg

Pythonを学んでいると、bytesという型を見かけることがあります。

ただ、このbytesは少し分かりにくい存在です。
見た目は文字列に似ているのに、実際の使い方は異なり、普段のコードではあまり意識して使う機会も多くはありません。

そのため、「何となく分からないまま」にしてしまいがちな部分でもあります。

この記事では、bytesという型が何なのかを、コードを動かしながら少しずつ理解していきます。

Pythonの導入についてはこちら。

Python入門:最初のプログラムを作って実行する方法 – Lean Data Office

Python環境構築のやり方|初心者向け完全ガイド – Lean Data Office

bytesとは

まず、bytesを一言で表すと、「データそのもの」を扱う型です。

文字列(str)は人間が読むためのデータですが、bytesはそうではありません。
意味を持った文字ではなく、0と1の並び、つまりバイナリデータをそのまま扱うためのものです。

実際に見てみると、少しイメージがしやすくなります。

text = "hello"
print(type(text))
image
data = b"hello"
print(type(data))
image

同じ「hello」でも、まったく別の型であることが分かります。
bがついている方がbytesです。

ここで重要なのは、bytesの中身は文字ではなく、単なる数値の並びとして扱われているという点です。

次に、bytesを直接作ってみます。

data = bytes(4)
print(data)
image

この結果を見ても、最初は少しピンと来ないかもしれません。

これは「4バイト分のデータ」を作っている状態です。
\x00というのは16進数で0を表しており、それが4つ並んでいます。

つまりここでできているのは、「値がすべて0のデータ領域」であって、文字列ではありません。

こう考えると、bytesは「意味を持つ前の状態のデータ」として捉えることができます。

文字列をbytesに変換する

通常、私たちは文字列を扱うことが多いので、これをbytesに変換する場面が出てきます。
このときに使うのがencodeです。

text = "🙂"
data = text.encode("utf-8")
print(data)
image

この結果は、文字がバイト列に変換されたものです。

ここで大切なのは、「utf-8」という指定です。
これはエンコードのルールで、文字をどのように数値に変換するかを決めています。

同じ文字でも、このルールが変わるとバイト列の内容も変わります。
つまり、bytesだけを見ても、それが何のデータなのかは分かりません。

bytesを文字列に戻す逆に、bytesをもう一度文字として扱いたい場合は、decodeを使います。

decoded = data.decode("utf-8")
print(decoded)
image

ここでも先ほどと同じように、エンコードのルールを指定します。
このルールが一致していないとうまく元に戻すことができず、文字化けが起きます。

そのため、encodedecodeはセットで考える必要があります。

bytesの特徴とbytearray

bytesにはもう一つ重要な特徴があります。
それは、一度作ると中身を変更できないことです。

data = b"hello"
data[0] = 65
image

このコードはエラーになります。

これは、bytesがイミュータブル(変更不可)なオブジェクトだからです。
文字列やタプルと同じように、作成後に値を書き換えることはできません。

もし中身を変更したい場合は、bytearrayという型を使います。

data = bytearray(b"hello")
print(data)
image

bytearraybytesと似ていますが、こちらは中身を書き換えることができます。

data[0] = 72
image

このとき指定している「72」という値は、文字そのものではなく数値です。
バイト列では、それぞれの要素が数値として扱われています。

そのため、文字を直接扱っている感覚とは少し違う操作になります。

補足:内部の動きを見てみる

最後に、少しだけ面白い例を見てみます。

emoji = bytearray("🙂".encode("utf-8"))
print(emoji)
image

この状態で、最後の値を変えてみます。

emoji[3] = 0x83
print(emoji.decode("utf-8", errors="replace"))
image

結果は環境によって異なりますが、元の絵文字とは違う表示になります。

これは、バイト列の一部が変わったことで、「別のデータ」として解釈されるためです。

この例からも分かるように、bytesはそれ単体では意味を持たず、どのように解釈するかによって結果が変わります。

bytesは最初は扱いにくく感じるかもしれませんが、考え方自体はシンプルです。

文字列は人間のためのデータであり、bytesはコンピュータがそのまま扱うためのデータです。
そして両者は、エンコードとデコードによって行き来します。

普段はあまり意識しない部分ですが、ファイルの読み書きや通信処理では必ず登場します。

このあたりが自然に理解できるようになると、Pythonだけでなくプログラミング全体の理解も少し深まってきます。

筋トレとオートメーションが趣味。 モバイルアプリ個人開発者。 データ処理・可視化とレポートにハマり備忘録と情報共有のためにブログ開設し運営している。