Pythonの辞書と配列の比較 – Deep Dive

このガイドでは、Pythonの最も人気のある2つのデータ構造、辞書と配列について見ていきます。

これらはそれぞれ、特定のタスクのための長所と短所を持つ、データを配置する特定の方法を提供し、いつ使用するかを知ることは、あなたが組み込み機能を活用することを可能にします。

Note: このガイドは Python 3.x を想定しており、そのほとんどはそれ以降のバージョンを対象としています。

しかし、Python 2.xとの主な違いもいくつか挙げておきます。

Pythonの配列のガイド

配列はコンピュータサイエンスの基本的なデータ構造の1つで、0〜n個の要素の並びで、各要素はインデックスを持っています。

ほとんどの配列は固定サイズなので、新しい配列が作成されるたびにメモリが消費されます。

ここでは、7個の要素からなる単純な配列を用意しました。

配列のインデックスは通常0から始まり、各要素にはアクセスに使用する位置のインデックスがあります。

このため、配列のアクセス時間計算量はO(1)となります。

Pythonの配列のほとんどは動的型付けされています。

つまり、配列のオブジェクトは型を持っていますが、配列自体は1つの型だけに制限されていません。

整数、文字列、オブジェクトからなる配列や、同様に異種混合の別の配列を持つことができます。

Pythonには6つの重要な配列の型があります。

リスト、タプル、ストリング、バイト、バイトアレイ、そしてarray.array` です。

それぞれの配列について説明するとき、考慮すべきいくつかの重要な性質があります。

  • 動的かそうでないか
  • 静的な型付けか動的な型付けか
    静的型付けか動的型付けか * 変数型付けか不変型か

Pythonのリスト

Pythonのリストは動的(サイズが固定されていない)、動的型付け(要素が単一の型に制限されていない)、Mutable(要素がインプレースで変更できる)である。

Pythonでは、リストは角括弧[]の中で要素を宣言することで定義されます。

それでは、リストを定義してみましょう。

myList = [1, 2, 3, "Mark", "John", "Emma"]
print(myList)


このリストには、いくつかの整数と、名前を表すいくつかの文字列が含まれています。

リストは動的に型付けされるので、これは許されます。

[1, 2, 3, 'Mark', 'John', 'Emma']


リストは動的なので,例えば新しい要素を追加して要素の数を変えることができます.

myList.append(4)
myList.append("Peter")
print(myList)


この結果、リストには最初に定義した6つの要素ではなく、8つの要素が含まれることになります。

[1, 2, 3, 'Mark', 'John', 'Emma', 4, 'Peter']


では、要素を置き換えて、新しい要素を追加してみましょう。

リストのID(メモリ上の参照)をチェックして、追加された要素や置き換えられた要素を含む新しいコピーと入れ替わらないことを裏から確認することにします。

myList = [1, 2, 3, "Mark", "John", "Emma", 4, "Peter"]
# Print original list and its ID
print('Original list: ', myList)
print('ID of object in memory: ', id(myList))


# Modify existing element and add a new one
myList[4] = "Anna"
myList.append("Dan")


# Print changed list and its ID
print('Changed list: ', myList)
print('ID of object in memory: ', id(myList))


このコードを実行すると、次のようになります。

Original list:  [1, 2, 3, 'Mark', 'John', 'Emma', 4, 'Peter']
ID of object in memory:  140024176315840
Changed list:  [1, 2, 3, 'Mark', 'Anna', 'Emma', 4, 'Peter', 'Dan']
ID of object in memory:  140024176315840


myListがメモリ上の同じオブジェクト (140024176315840`) を指しているという事実は、リストがいかに変更可能であるかをさらに示しています。

注:Pythonのリストは、関数を順番に格納することもできます。

def f1():
    return "Function one"


def f2():
    return "Function two"


def f3():
    return "Function three"


listOfFunctions = [f1, f2, f3]
print(listOfFunctions)


という結果になります。

[<function 0x0000016531807488="" at="" f1="", <function 0x00000165318072f0="" at="" f2="", <function 0x0000016531807400="" at="" f3=""]


出力は与えられたアドレスの関数からなります。

では、関数にアクセスし、それを実行してみましょう。

print(listOfFunctions[0]())


このリストの最初の要素は f1() ですので、その適切な print() 文が実行されることを期待します。

Function one


リストはPythonで最もよく使われる配列の一種です。

リストは使いやすく、直感的です。

また、要素にアクセスするための時間計算量はO(1)です。

Pythonのタプル

Pythonのタプルは非動的(サイズが固定)、動的型付け(要素が単一の型に制限されない)、不変(要素がインプレースで変更されない)である。

さらに、タプルを定義する際には、通常の括弧 () を使用する。

myTuple = (1, 2, 3, "Mark", "John", "Emma")
print(myTuple)


タプルは動的に型付けされるので、タプルの中に異なる型の要素を持つことができる。

(1, 2, 3, 'Mark', 'John', 'Emma')


タプルは非動的なので、サイズは固定されており、サイズを変更してしまうので、インプレースで要素を append() することはできません。

したがって、タプルは append() メソッドを持ちません。

しかし、より小さなタプルからなる新しいタプルを作成することはできます。

このタプルもサイズは固定です。

myTuple = (1, 2, 3)
anotherTuple = ("Mark", "John", "Emma")
print('Original tuple: ', myTuple)
print('ID of object in memory: ', id(myTuple))


myTuple = myTuple + anotherTuple
print('New tuple: ', myTuple)
print('ID of object in memory: ', id(myTuple))


これらのタプルを一緒に格納するために作られた新しいオブジェクトに、同じ変数参照を割り当てています。

参照変数は同じですが、メモリ上では全く異なるオブジェクトを指しています。

Original tuple:  (1, 2, 3)
ID of object in memory:  139960147395136


New tuple:  (1, 2, 3, 'Mark', 'John', 'Emma')
ID of object in memory:  139960147855776


タプル内の項目にアクセスするための時間計算量もO(1)である。

Pythonの文字列

Python 3 では、 str 型 (String の略) が Python 2 からオーバーホールされました。

Python 2では、文字列とバイトの両方を表現していましたが、Python 3では、この2つは全く異なるデータ型になりました。

Pythonの文字列は、非動的(サイズが固定)、静的型付け(要素が単一の型に制限)、不変(要素がその場で変更できない)です。

括弧 "" で囲まれた一連のバイト列(人間が読める文字列)が文字列の定義に使われます。

myStr = "qwerty"
print(myStr)


という結果になる。

qwerty


標準的な配列のインデックス付けによって要素にアクセスすることはできますが、変更することはできません。

print(myStr[0])
myStr[0] = "p"


これは次のようになります。

q
TypeError: 'str' object does not support item assignment


実は、文字列は再帰的です。

文字を使って文字列を宣言すると、各文字に対応する文字列が形成され、それが別の文字列を構成する文字列のリストに追加されます。

myStr`は長さ5で、長さ1の5つの文字列から構成されています。

myStr = "abcde"
print(len(myStr)) # Check the length of our str
print(type(myStr)) # Check the type of our str


print(myStr[0]) # Letter 'a'
print(len(myStr[0])) # Check the length of our letter
print(type(myStr[0])) # Check the type of our letter 'a'


という結果になります。

5
<class 'str'=""
a
1
<class 'str'=""


文字」と「文字列」はどちらも同じクラスである str です。

タプルと同様に、文字列を連結することができます。

その結果、2つの小さな文字列からなる新しい文字列ができます。

myStr = "qwerty"
myStr2 = "123"


result = myStr + myStr2
print(result)


そして、その結果は

qwerty123


繰り返しますが、文字列は文字のみをサポートし、他の型を混ぜることはできません。

myStr = "qwerty"
myStr2 = 123


result = myStr + myStr2
print(result)


という結果になります。

TypeError: can only concatenate str (not "int") to str


しかし、intや他のすべての型は、文字列表現にキャスト(変換)することができます。

myStr = "qwerty"
myStr2 = str(123) # int 123 is now casted to str


result = myStr + myStr2
print(result)


これは次のようになります。

qwerty123


この方法では、例えば intstring を同じ行に出力することができます。

myStr = "qwerty"
print("myStr's length is: " + len(myStr)) # TypeError


print("myStr's length is: " + str(len(myStr))) # String concatenation resulting in 'myStr's length is: 6'


Pythonのバイト数

Python のバイトは非動的 (サイズが固定)、静的型付け (要素が単一の型に制限される)、不変 (要素がその場で変更されない) である。

バイトオブジェクトは 0 から 255 (8 ビット) までの複数のシングルバイトまたは整数から構成されます。

タプルを明示的に bytes にキャストしなければならないので、 bytes オブジェクトの定義は他の配列とは少し異なります。

myBytes = bytes((0, 1, 2))
print(myBytes)


これは次のような結果になります。

b'x00x01x02'


タプルが異なる型の要素を含んでいる場合、 TypeError がスローされます。

myBytes = bytes((0, 1, 2, 'string'))


TypeError: 'str' object cannot be interpreted as an integer


strを扱う場合、bytes` の配列は文字セットでエンコードされていなければなりません。

そうでないと、何を表しているのかが曖昧になってしまいます。

もし bytes のエンコードがどのように行われるのかよく知らない場合は、 Python でバイトを文字列に変換する方法 を参照してください。

さらに、整数の配列である bytes は、 bytearray という別の配列型にキャストすることで mutable にすることができます。

Python Bytearray

PythonのBytearrayは動的(サイズが固定でない)、静的型付け(要素が単一の型に制限される)、mutable(要素がインプレースで変更できる)です。

myStr = "This is a string"


myBytes = bytes(myStr) # this will result in an error TypeError: string argument without an encoding


myBytes = bytes(myStr, 'utf-8')
print(myBytes) # this will print out myStr normally


では、この配列に要素を追加したり、要素を変更したりすることを試してみましょう。

myByteArray = bytearray((0, 1, 2))


この結果は

myByteArray = bytearray((0, 1, 2))
print(myByteArray)
print("ByteArray ID: ", id(myByteArray))


myByteArray.append(3)
print(myByteArray)
print("ByteArray ID: ", id(myByteArray))


myByteArray[3] = 50
print(myByteArray)
print("ByteArray ID: ", id(myByteArray))


これらはすべて同じオブジェクト ID を持ち、メモリ内の同じオブジェクトが変更されたことを意味します。

bytearraybytes` 配列にキャストして戻すことができますが、これは O(n) 時間かかる高価な処理であることを覚えておいてください。

Python array.array

ここまでは、組み込み型を使ってきました。

しかし、もう一つのタイプの配列が array モジュールとして存在します。

この array は動的で (サイズが固定されない)、静的な型付け (要素が 1 つの型に制限される)、そして mutable (その場で変更できる) です。

配列`で使用する型は明示的に指定する必要があり、これらの型は C スタイルの型である。

32 ビット整数、浮動小数点数、double などです。

これらの型にはそれぞれマーカーがあり、整数は i、浮動小数点数は f、倍数は d です。

ここでは、 array モジュールを使用して、整数の配列を作成してみましょう。

bytearray(b'x00x01x02')
ByteArray ID:  140235112668272


bytearray(b'x00x01x02x03')
ByteArray ID:  140235112668272


bytearray(b'x00x01x022')
ByteArray ID:  140235112668272


C 言語でよく使われる型

Python辞書ガイド

辞書はPythonの中心的なデータ構造です。

キーと値のペアでデータを格納します。

このため、マップ、ハッシュマップ、ルックアップテーブルと呼ばれることもあります。

辞書には、いくつかの異なる種類があります。

  • dict
  • collections.defaultdict (コレクションズデフォルトディクショナリ)
  • collections.OrderedDict (日本語)
  • collections.ChainMap です。

辞書は、ルックアップ操作のキーを特定するためのハッシュ値に依存しています。

ハッシュテーブルは多くのハッシュ値を持ちますが、それらはハッシュテーブルのライフタイム中、決して変更されることはありません。

ハッシャブル型とハッシュ値

すべてのオブジェクトはハッシュ値を持っており、 hash() メソッドを用いてハッシュ値を取得することができます。

この値は一定ではなく、実行時に計算されますが、a == bの場合、hash(a) は常に hash(b) と等しくなります。

import array


myArray =  array.array("i", (1, 2, 3, 4))


このコードは、次のような結果になります。

randomString = "This is a random string"
a = 23
b = 23.5
print(hash(randomString))
print(hash(a))
print(hash(b))


注意: 等しい数値は、その型に関係なく同じハッシュ値を持ちます。

4400833007061176223
23
1152921504606846999


のような結果になります。

a = 23
b = 23.0
print(hash(a))
print(hash(b))


このメカニズムにより、Pythonの辞書は非常に高速になります。

各要素に一意の識別子があり、ルックアップ時間がO(1)となります。

Python辞書

辞書の内容 (dict 型) は中括弧 {} の中で定義されます。

構文はJSONに似ており、キーと値のペアを指定する。

23
23


辞書は任意の数のペアを持つことができ、キーは重複することなくハッシュ化できる必要がある (重複したキーは同じハッシュになる)。

このような場合、最初のキーは拒否され、辞書には実際には2番目のキーしか含まれない。

辞書は変更可能なので、存在しないキーに「アクセス」してその値を設定するだけで、新しいキーと値のペアを追加することができる。

myDict = {
    "name": "Mike James",
    "age": 32,
    "country": "United Kingdom"
}


これは次のような結果になる。

myDict["countries_visited"] = ["Spain", "Portugal", "Russia"]
print(myDict)


Pythonのコアである dict はおそらくほとんどの問題を解決してくれますが、そうでない場合は、 collections というライブラリからインポートできるいくつかの辞書型があります。

Python DefaultDict

ディクショナリ`を使用する際に遭遇する可能性のある問題は、存在しないキーの値にアクセスしようとすることです。

例えば、先ほどのデモで print(myDict["zip_code"]) にアクセスすると、 zip_code が存在しないため、 KeyError: zip_code が発生します。

このとき、 defaultdictdefault_factory – キーが存在しない場合にデフォルトの値を返す関数 – を要求します。

このように、 defaultdict は決して KeyError を発生させることができません。

{'name': 'Mike James', 'age': 34, 'country': 'United Kingdom', 'countries_visited': ['Spain', 'Portugal', 'Russia']}


これは予想通り、次のような結果になります。

from collections import defaultdict


def safe_function(): # default_factory
    return "Value not defined"


myDict = defaultdict(safe_function)
myDict["name"] = "Mark James"
myDict["age"] = 32


print(myDict["country"]) # This will output Value not defined and not raise a KeyError


defaultdictの値の定義は、コアのdict` クラスとは異なります。

なぜなら、すべてのキーと値のペアを ‘手動’ で定義する必要があり、JSON ライクな構文よりも面倒な作業だからです。

Python ChainMap

このタイプの辞書は、複数の辞書を1つにつなげる、つまり連鎖させることができます。

データにアクセスする際、最初に正しいキーを見つけるまで、一つずつキーを探します。

Value not defined


この結果、ChainMapができあがります。

from collections import ChainMap


myDict1 = {
    "name": "Mike James",
    "age": 32
}


myDict2 = {
    "name": "James Mike",
    "country": "United Kingdom",
    "countries_visited": ["Spain", "Portugal", "Russia"]    
}


myDictResult = ChainMap(myDict1, myDict2)
print(myDictResult)


注意: 重複するキーを定義することができる。

name’は両方の辞書に存在する。

ChainMap({'name': 'Mike James', 'age': 32}, {'name': 'James Mike', 'country': 'United Kingdom', 'countries_visited': ['Spain', 'Portugal', 'Russia']})


にアクセスしようとすると、最初にマッチするキーが見つかります。

print(myDictResult['name'])


また、今はコアの dict を扱っているので、これらはまだ KeyError を発生させる可能性があることも覚えておいてください。

Python OrderedDict

Note: Python 3.6では、辞書はデフォルトで挿入順になっています。

OrderedDictは、辞書のキーと値のペアの挿入順序を維持したい場合に使用します。

dict` はこれを保証するものではなく、時系列とは異なる挿入順序になってしまう可能性があります。

もし、これが重要でないのであれば、快適に辞書を使用することができます。

しかし、日付を扱う場合など、この点が重要な場合は、代わりに OrderedDict を使用することになるでしょう。

Mike James


この結果は

from collections import OrderedDict


orderedDict = OrderedDict()
orderedDict['a'] = 1
orderedDict['b'] = 2
orderedDict['c'] = 3
orderedDict['d'] = 4

print(orderedDict)


Note: Python 3.6 では dict オブジェクトは挿入順を保持しますが、挿入順が必要な場合は OrderedDict を使用してください。

通常の dict を使用すると、他の Python バージョン (それ以前) のコードで挿入順が保証されません。

ディクショナリーメソッドとアレイメソッドの比較

さて、物事のコツをつかんだところで、この2つの型に実装されているすべてのメソッドを網羅する必要があります。

データに対する基本的な操作は、アクセス(取得)、更新、追加、削除の4つです。

それでは、これから実験する配列と辞書を定義してみましょう。

OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])


データ取得中

ディクショナリー 辞書内のデータにアクセスする方法は複数ある。

  • キー名を参照する方法 – myDict["key_name"]:

  • “`
    exampleDict = {
    “id”: 101,
    “name”: “Marc Evans”,
    “date_of_birth”: “13.02.1993.”,
    “city”: “Chicago”,
    “height”: 185,
    }

exampleArray = [1, 2, 3, “red”, “green”, “yellow”, “blue”, 4]

``
*
get()メソッドを呼び出す -myDict.get(“key_name”)`:

  • “`
    print(exampleDict[“name”])
    # Output: Marc Evans

    ``
    * 辞書のすべてのキーにアクセスする -
    myDict.keys()` – キーのリストを返します。

  • “`
    print(exampleDict.get(“city”))
    # Output: Chicago

    ``
    * 辞書の全ての値にアクセスする -
    myDict.values()` – 値のリストを返します。

  • “`
    print(exampleDict.keys())
    # Output: dict_keys([‘id’, ‘name’, ‘date_of_birth’, ‘city’, ‘height’])

    ``
    * すべてのキーと値のペアにアクセスする:
    myDict.items()` – キーと値のペアのタプルを返します。

  • “`
    print(exampleDict.values())
    # Output: dict_values([101, ‘Marc Evans’, ‘13.02.1993.’, ‘Chicago’, 185])

    “`

配列です。

配列からデータを取得する方法は1つだけです。

配列からデータを取得する方法は1つだけです: * 要素のインデックス – myArray[index_number] を参照する。

  • “`
    print(exampleDict.items())
    # Output: dict_items([(‘id’, 101), (‘name’, ‘Marc Evans’), (‘date_of_birth’, ‘13.02.1993.’), (‘city’, ‘Chicago’), (‘height’, 185)]

    “`

データの更新

辞書 辞書のデータを更新するには、2つの方法があります。

  • あるキーに直接新しい値を設定する – myDict["key"] = new_value:

  • “`
    print(exampleArray[3])
    # Output: red

    ``
    * update()
    メソッドを呼び出す – myDict.update({"key": new_value}) – メソッドの引数は辞書である必要があります。

  • “`
    exampleDict[“height”] = 190
    print(exampleDict[“height”])
    # Output: 190

    “`

配列です。

配列がMutableであれば、データを取得するのと同じような方法で変更することができます。

  • myArray[index_number] = new_value` – メソッドの引数は辞書でなければならない: + “`
    exampleDict.update({“height”: 190})
    print(exampleDict[“height”])
    # Output: 190

    “` Array: 配列がミュータブルであれば、データの取得と同様の方法で変更することができます。

  • “`
    exampleArray[3] = “purple”
    print(exampleArray)
    # Output: [1, 2, 3, ‘purple’, ‘green’, ‘yellow’, 4, ‘blue’]

    “`

データ追加

辞書にデータを追加します。

辞書にデータを追加するには、2つの方法がある。

  • 新しいキーに値を設定すると、自動的にキーと値のペアが作成され、追加されます。

  • “`
    exampleDict[“age”] = 45
    print(exampleDict)
    # Output: {‘id’: 101, ‘name’: ‘Marc Evans’, ‘date_of_birth’: ‘13.02.1993.’, ‘city’: ‘Chicago’, ‘height’: 185, ‘age’: 45}

    ``
    * update()
    メソッドを呼び出す – myDict.update({"new_key": value}).update()メソッドを呼び出す: myDict.update({"new_key": value}):

  • “`
    exampleDict.update({“age”: 45})

    “`

配列です。

配列にデータを追加する方法はいくつかあります(ただし、配列はmutableである必要があります)。

  • append() メソッドを呼び出す – myArray.append(new_element)new_elementmyArray の末尾に追加することができます。

  • “`
    exampleArray.append(“grey”)
    print(exampleArray)
    # Output: [1, 2, 3, “purple”, “green”, “yellow”, “blue”, 4, “grey”]

    ``
    *
    insert()メソッドの呼び出し -myArray.insert(index_number, new_element)-new_elementindex_number` 位置に挿入します。

  • “`
    exampleArray.insert(0, 0)
    print(exampleArray)
    # Output: [0, 1, 2, 3, “purple”, “green”, “yellow”, “blue”, 4, “grey”]

    ``
    *
    extend()メソッドの呼び出し -myArray.extend(myArray2)-myArray2の要素をmyArray` の末尾に挿入します。

  • “`
    exampleArray2 = [5, 6]
    exampleArray.extend(exampleArray2)
    print(exampleArray)
    # Output: [0, 1, 2, 3, “purple”, “green”, “yellow”, “blue”, 4, “grey”, 5, 6]

    “`

データの削除

辞書 辞書からデータを削除するには、複数の方法があります。

  • メソッド pop() の呼び出し – myDict.pop("key_name") – 削除するキーの名前を指定します。

  • “`
    exampleDict.pop(“name”)
    print(exampleDict)

    {‘id’: 101, ‘date_of_birth’: ‘13.02.1993.’, ‘city’: ‘Chicago’, ‘height’: 185}

    ``
    *
    popitem()メソッドの呼び出し -myDict.popitem()` – Python 3.7+ では最後に追加された key-value ペアを、Python 3.7 以下のバージョンではランダムな key-value ペアが削除されます。

  • “`
    exampleDict.popitem()
    print(exampleDict)

    {‘id’: 101, ‘name’: ‘Marc Evans’, ‘date_of_birth’: ‘13.02.1993.’, ‘city’: ‘Chicago’}

    ``
    *
    delキーワードを使用する -del myDict[“key_name”]`.

  • “`
    del exampleDict[‘name’]
    print(exampleDict)

    {‘id’: 101, ‘date_of_birth’: ‘13.02.1993.’, ‘city’: ‘Chicago’, ‘height’: 185}

    del dict deletes the entire dictionary

    del exampleDict
    print(exampleDict)

    NameError: name ‘exampleDict’ is not defined

    ``
    * clear()
    メソッドを呼び出す – myDict.clear() – 辞書は空になりますが、まだ空の辞書として存在します {}.

  • “`
    exampleDict.clear()
    print(exampleDict)

    {}

    “`

配列です。

配列からデータを削除するには、いくつかの方法があります。

  • メソッド pop() を呼び出す – myArray.pop(index_number) – 指定した index_number にある要素を削除する。

  • “`
    exampleArray.pop(2)
    print(exampleArray)

    [1, 2, ‘red’, ‘green’, ‘yellow’, ‘blue’, 4]

    ``
    *
    remove()メソッドの呼び出し -myArray.remove(value)- 指定されたvalue` を持つ最初のアイテムを削除します。

  • “`
    exampleArray.remove(2)
    print(exampleArray)

    [1, 3, ‘red’, ‘green’, ‘yellow’, ‘blue’, 4]

    ``
    *
    clear()メソッドの呼び出し -myArray.clear()- 辞書と同じように、配列からすべての要素を削除して、空の配列[]` を残します。

  • “`
    exampleArray.clear()
    print(exampleArray)

    []

    “`

</function

タイトルとURLをコピーしました