当サイトは、アフィリエイト広告を利用しています
Pythonで複数の値を扱う際は
の3つを扱うことになる
いずれもコレクション型だが、内部構造・計算量・用途は明確に異なる。
本記事では
を体系的に整理する。
listの特徴としては
がある。
そのため、用途としては
を想定している。
listの存在判定は線形探索(O(n))である。
つまり、 頭から順に走査する必要がある。
作成から追加、削除などの基本的な操作をまとめておく。
# リテラルlst:str = []lst:str = ["A", "B", "C"]# listコンストラクタlst = list()lst = list([1, 2, 3])lst = list("abc") # ['a', 'b', 'c']# 他のイテラブルから変換するときtuple_data = (1, 2, 3)lst = list(tuple_data)# リスト内包表記(実務で多用)lst = [i for i in range(5)]
色々作り方がある。
# 作成lst = [1, 2, 3] # [1, 2, 3]# 末尾に追加lst.append(4) # [1, 2, 3, 4] # 追加# 末尾に複数追加# イテラブルを展開して追加されるlst.extend([5, 6, 7]) # [1, 2, 3, 4, 5, 6, 7] # 追加# appendでやった場合lst.append([5, 6, 7]) # [1, 2, 3, 4, [5, 6, 7]] # 追加# 指定インデックスに追加# 第一引数はインデックス、第二引数は追加する値lst.insert(2, 10) # [1, 2, 10, 3, 4, 5, 6, 7] # 追加# スライス代入(値)lst[2:2] = [10] # [1, 2, 10, 3, 4, 5, 6, 7] # 追加# スライス代入(イテラブル)# lst[2:2] は「空スライス」# そこにイテラブルを代入すると# その位置に展開挿入されるlst[2:2] = [10, 20] # [1, 2, 10, 20, 3, 4, 5, 6, 7] # 追加
# 作成lst = [1, 2, 3] # [1, 2, 3]# 末尾の要素を取得lst[-1] # 3# 指定インデックスの要素を取得lst[1] # 2# スライス# sequence[start:stop:step]# start:開始インデックス(含む)# stop:終了インデックス(含まない)# step:間隔(省略可)# インデックス範囲を指定して取得(スライス)lst[1:3] # [2, 3]# インデックス範囲を指定して取得(スライス)lst[:3] # [1, 2, 3]# インデックス範囲を指定して取得(スライス)lst[1:] # [2, 3]
# 作成lst = [1, 2, 3] # [1, 2, 3]# 末尾の要素を削除del lst[-1] # [1, 2]# 指定インデックスの要素を削除del lst[1] # [1, 3]# スライス削除# sequence[start:stop:step]# start:開始インデックス(含む)# stop:終了インデックス(含まない)# step:間隔(省略可)del lst[1:3] # [1, 3]# インデックスではなく、値を指定して削除する# 最初に見つかった値を削除# 見つからない場合はエラー(valueError)lst.remove(3) # [1, 2]# 末尾の要素を取り出す# 取り出すとなくなるlst.pop() # [1, 2]# 要素をインデックスで取り出す# 取り出すとなくなるlst.pop(0) # [2]
setの特徴としては
がある。
用途は
が想定される
平均O(1)になる。
setはハッシュ値を計算して値を格納する。
そのため、listなどの内部構造が動的配列なものに比べると
高速な操作が可能になる。
hashableとは、
hash()を呼び出すことが可能なオブジェクトを指す。
要はハッシュ値が計算できて、かつ不変であるオブジェクトのこと。
※変更によってハッシュ値が変わらないこと
具体的には
が該当する
逆にhashableでなのは
が該当する。
理由は可変でだから
# list作成x = [1,2]# listをsetに入れるs = {x}# listに要素を追加x.append(3)
これを実行すると
TypeError: unhashable type: 'list'というエラーが出る。
理由は元のハッシュ値と内容が一致しなくなるから
だから Python は最初から禁止している。
作成から追加、削除、集合演算などの基本的な操作をまとめておく。
# 作成# リテラルs = {1, 2, 3} # {1, 2, 3}# コンストラクタで作成# listからsetに変換s = set([1, 2, 3]) # {1, 2, 3}# set内包表記s = {i for i in range(5)} # {0, 1, 2, 3, 4}
# 作成s = {1, 2, 3}# 追加※単数のみs.add(4) # {1, 2, 3, 4}# 追加(複数)# イテラブルを展開して追加される※内部的には1要素ずつ、addしているs.update([5, 6]) # {1, 2, 3, 4, 5, 6}
# 作成s1 = {1, 2, 3}# 取得(ループ)for n in s1:print(n) # 1, 2, 3# 取得(リスト化)l = list(s1) # [1, 2, 3]# ランダムで1つ取得l = s1.pop() ## 条件で取り出す(set内包表記)s = {x for x in s1 if x % 2 == 0} # {2}
set は順序を持たないためインデックスでの取得ができず、
また特定の値を「取り出す」操作も基本的には削除を伴うため、
位置を基準に柔軟に扱える listと比べると取り出しの自由度は低く、扱いづらく感じる場合がある。
# 作成s1 = {1, 2, 3}# 値を指定して削除# 値が存在しない場合はkeyErrors1.remove(2) # {1, 3}# 値がなくてもs1.discard(4) # {1, 3}# 全削除s1.clear() # set()
条件でいうとORをとるのと同等
# 作成s1 = {1, 2, 3}s2 = {3, 4, 5}# 和集合s3 = s1 | s2 # {1, 2, 3, 4, 5}# またはs3 = s1.union(s2) # {1, 2, 3, 4, 5}# 破壊的な和集合s1 |= s2 # {1, 2, 3, 4, 5}# またはs1.update(s2) # {1, 2, 3, 4, 5}
|演算子はunionメソッドを呼び出す |=演算子はupdateメソッドを呼び出す これは引き算してるイメージ
# 作成s1 = {1, 2, 3}s2 = {3, 4, 5}# 差集合s3 = s1 - s2 # {1, 2}# またはs3 = s1.difference(s2) # {1, 2}# 破壊的な差集合s1 -= s2 # {1, 2}# またはs1.difference_update(s2) # {1, 2}
-演算子はdifferenceメソッドを呼び出す -=演算子はdifference_updateメソッドを呼び出す 条件でいうとANDをとるのと同等
# 作成s1 = {1, 2, 3}s2 = {3, 4, 5}# 積集合s3 = s1 & s2 # {3}# またはs3 = s1.intersection(s2) # {3}# 破壊的な積集合s1 &= s2 # {3}# またはs1.intersection_update(s2) # {3}
&演算子はintersectionメソッドを呼び出す &=演算子はintersection_updateメソッドを呼び出す 両方にある値だけを除外する
# 作成s1 = {1, 2, 3}s2 = {3, 4, 5}# 対称差集合s3 = s1 ^ s2 # {1, 2, 4, 5}# またはs3 = s1.symmetric_difference(s2) # {1, 2, 4, 5}# 破壊的な対称差集合s1 ^= s2 # {1, 2, 4, 5}# またはs1.symmetric_difference_update(s2) # {1, 2, 4, 5}
^演算子はsymmetric_differenceメソッドを呼び出す ^=演算子はsymmetric_difference_updateメソッドを呼び出す tupleの特徴としては
がある。
そのため、用途としては
を想定している。
作成、取り出し操作をまとめておく。
# リテラルtup = (1, 2, 3) # (1, 2, 3)# コンストラクタtup = tuple([1, 2, 3]) # (1, 2, 3)tup = tuple('Hello') # ('H', 'e', 'l', 'l', 'o')# 関数tup = tuple(range(10)) # (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
# リテラルtup = (1, 2, 3) # (1, 2, 3)# インデックスtup[0] # 1tup[-1] # 3# スライスtup[1:3] # (2, 3)tup[1:] # (2, 3)tup[:2] # (1, 2)tup[:] # (1, 2, 3)# アンパックa, b, c = tup # a = 1, b = 2, c = 3
簡潔にまとめると
重要なのは、 データの性質(順序・変更有無・重複・判定頻度)から逆算して型を選ぶこと。
なので設計観点での型選択基準として
を設計段階で考える必要がある。
日常的にはlistを目にする場面が多いが、要件によっては set や tuple が中心になることもある。
「とりあえずlist」で始めて、用途が見えた段階で型を確定させるのも現実的かな。