/
Time Limit: 0 msec / Memory Limit: 0 KiB
キーポイント
- Python は多くのデータ型を備えており、特に代表的なものに
list,tuple,set,dictが存在する - 扱いたいデータや処理の特徴に応じて使い分けることが望ましい
list / tuple
データの列を扱うことのできるデータ構造
list/tupleの主要な操作
| 操作 | 記法 | 計算量 | list |
tuple |
|---|---|---|---|---|
| 値のアクセス | l[i] |
O(1) | ○ | ○ |
| 値の変更 | l[i] = x |
O(1) | ○ | |
| 末尾への値の挿入 | l.append(x) |
O(1) | ○ | |
| 末尾以外への値の挿入 | l.insert(i, x) |
O(N) | ○ | |
| 末尾の値の削除 | l.pop() |
O(1) | ○ | |
| 末尾以外の値の削除 | l.pop(i) |
O(N) | ○ | |
| 列の長さの取得 | len(l) |
O(1) | ○ | ○ |
| 値の存在判定 | x in l |
O(N) | ○ | ○ |
| 値の出現回数 | l.count(x) |
O(N) | ○ | ○ |
- コード例
# list
l = [3, 1, 4, 1, 5]
print(l[2]) # 4
l[2] = 7
print(l) # [3, 1, 7, 1, 5]
l.append(9)
print(l) # [3, 1, 7, 1, 5, 9]
l.insert(3, 8)
print(l) # [3, 1, 7, 8, 1, 5, 9]
l.pop()
print(l) # [3, 1, 7, 8, 1, 5]
l.pop(0)
print(l) # [1, 7, 8, 1, 5]
print(len(l)) # 5
print(1 in l) # True
print(3 in l) # False
print(l.count(1)) # 2
# tuple
t = (3, 1, 4, 1, 5)
print(t[2]) # 4
print(len(t)) # 5
print(1 in t) # True
print(2 in t) # False
print(t.count(3)) # 1
- [Appendix] その他の操作(Python 公式ドキュメント)
set
集合を扱うことのできるデータ構造
setの主要な操作
| 操作 | 記法 | 計算量 1 |
|---|---|---|
| 値の追加 | s.add(x) |
O(1) |
| 値の削除 | s.remove(x) |
O(1) |
| 集合の要素数の取得 | len(s) |
O(1) |
| 値の存在判定 | x in s |
O(1) |
- コード例
s = {1, 3, 5}
s.add(2)
print(s) # {1, 2, 3, 5}
s.remove(3)
print(s) # {1, 2, 5}
print(len(s)) # 3
print(2 in s) # True
print(3 in s) # False
- [Appendix] その他の操作(Python 公式ドキュメント)
dict
辞書や連想配列と呼ばれるデータ構造
dictの主要な操作
| 操作 | 記法 | 計算量 1 |
|---|---|---|
| 値のアクセス | d[k] |
O(1) |
| 値の追加・更新 | d[k] = v |
O(1) |
| キーの削除 | d.pop(k) |
O(1) |
| 辞書の項目数 | len(d) |
O(1) |
| キーの存在判定 | k in d |
O(1) |
- コード例
d = {3: 31, 1: 31, 4: 30}
d[11] = 30
print(d) # {3: 31, 1: 31, 4: 30, 11: 30}
d.pop(4)
print(d) # {3: 31, 1: 31, 11: 30}
print(len(d)) # 3
print(11 in d) # True
print(4 in d) # False
print(31 in d) # False
- [Appendix] その他の操作(Python 公式ドキュメント)
list
1.10.リスト で扱ったように、list はデータの列を扱うデータ構造です。
キーポイントで触れたように値のアクセス・変更・挿入・削除などができます。
これらは詳しくは 1.10.リスト で扱っていますので、ぜひ復習してみてください。
tuple
tuple も list と同様にデータの列を扱うデータ構造です。list との最大の違いは、(文字列 (str) などと同様に)変更ができないことです。
t = (3, 1, 4, 1, 5) # エラーになる t[0] = 9
エラー出力
Traceback (most recent call last):
File "/judge/Main.py", line 3, in <module>
t[0] = 9
~^^^
TypeError: 'tuple' object does not support item assignment
(意訳:tuple の要素への代入は行えません)
ただ、あくまで tuple の中身の変更ができないだけで、その変数に代入されている tuple 自体を別のものに変更することはできます。
t = (3, 1, 4, 1, 5) # これは OK t = (9, 8, 7)
中身を変更するような操作を除き、tuple でも list と同じような操作を行うことができます。
tuple を作る
tuple()で空のtupleを作ることができます。tupleはlistとは異なり要素を追加することができないので、使うことはほとんどないと思います。
( 要素1, 要素2, 要素3, ... )でtupleを作ることができます。tuple( for 文に入れられるもの )でtupleを作ることができます。tuple([1, 2, 3, 4, 5])などといったように、listをtupleに変換することもできます。
tuple( 内包表記 )でtupleを作ることができます。
# tuple (3, 1, 4, 1, 5) を作り、t に代入する
t = (3, 1, 4, 1, 5)
# いろいろな型を持つデータを 1 つの tuple に入れることができる
t = ("Hello", "AtCoder", 123, 4.5, [])
# 括弧は省略することもできる
t = "Hello", "AtCoder", 123, 4.5, []
# tuple (0, 1, 2, 3, 4) を作る
t = tuple(range(5))
# tuple (0, 1, 4, 9, 16) を作る
t = tuple(i * i for i in range(5))
tuple を作るときの注意点
1 要素の tuple
- 1 要素の
tupleを作るときは、ただ括弧でくくっているだけと区別するため、明示的にカンマをつける必要があります。
t = (3) # カンマをつけないとただ括弧でかこっているだけとされてしまい tuple にならない print(type(t)) # <class 'int'> t = (3,) # 1 要素の tuple を作るときは明示的に最後のカンマをつける必要がある print(type(t)) # <class 'tuple'> t = 3, # 1 要素の tuple を作るときでも括弧は省略することもできる print(type(t)) # <class 'tuple'>
内包表記での初期化
- 内包表記で tuple を初期化するとき、
()でくくるだけだとgeneratorという別のものを作る記法になってしまうため、tuple()でくくる必要があります。
t = [i for i in range(5)] # list ならこの書き方で OK print(type(t)) # <class 'list'> t = (i for i in range(5)) # 上と同じように括弧だけ変えると tuple にならない print(type(t)) # <class 'generator'> t = tuple(i for i in range(5)) # 内包表記で tuple を作るにはこのように書く必要がある print(type(t)) # <class 'tuple'>
tuple の長さ
tuplet に対し、len(t)で t の長さ(要素数)を取得できます。
i 番目の要素
tuplet と整数 i に対し、t[i]で t の i 番目の要素を取得できます。- この i のこと(リストの何番目の要素かを表す番号)を添字(そえじ)と言います。
- 添字は 0 から始まることに注意しましょう。先頭の要素は
t[0]で、末尾の要素はt[len(t)-1](またはt[-1])で取得することができます。
tupleはt[i] = xなどといったような 要素の変更はできない ことに注意しましょう。
for 文で要素を列挙する
tuplet に対し、for x in tで t の要素を順に x に代入して繰り返し処理を行うことができます。
t = (3, 1, 4, 1, 5)
for x in t:
print(x)
出力
3 1 4 1 5
出力する
tuplet に対し、print(*t)で t の要素を空白区切りで出力できます。
t = (3, 1, 4, 1, 5)
# t の要素を空白区切りで出力する
print(*t)
# t の要素を改行区切りで出力する
for x in t:
print(x)
出力
3 1 4 1 5 3 1 4 1 5
tuple のさらなる機能
tuple においても、1.10.リスト - リストのさらなる機能 で紹介した機能の多くを使うことができます(ただし、append, insert, pop などのような中の要素を変更するような機能は使えません)。
packing, unpacking
tuple は、複数の値をひとつの変数に格納していると見ることもできますが、逆に tuple の各要素を別々の変数に格納することもできます。これを、それぞれ packing, unpacking と呼びます。
# 複数の値をひとつの変数に代入 (packing) t = 3, 1, 4, 1, 5 # tuple の各要素を別々の変数に代入 (unpacking) a, b, c, d, e = t # これも OK a, b, c, d, e = (3, 1, 4, 1, 5) # もちろんこれも OK a, b, c, d, e = 3, 1, 4, 1, 5
なので、1.04.変数と型 - 複数の変数への代入 で紹介した x, y = 2, 3 というコードは、(2, 3) という tuple を作り、それを x と y に unpacking していたということになります。
また、x, y = y, x についても同様です。
なお、特に unpacking については list などの他のコンテナでも同様に行えます。
set
set はデータの集合を扱うデータ構造です。集合とは、 重複する要素をもたない、順序づけられていない 要素の集まりです。
Python では、データに対し「ハッシュ値」というものを定義することで集合を扱うデータ構造を実現しています(set を使う上で、基本的には「ハッシュ値」の仕組みについて理解する必要はありません)。
set のメリット
- 追加・削除・存在判定などの操作が高速に行える
set のデメリット
- 同じ要素を複数格納することができない
- 順序を保持することができない
- つまり「i 番目に追加した要素を求める」といったことは基本的にはできない
- また、「i 番目に大きい要素を求める」といったこともできない
例えば、以下のようなコードを書くことができます。
# 30, 10, 40 という 3 要素から set を作り、s に代入する
s = {30, 10, 40}
# s に 50 という要素を追加
s.add(50)
# s に 50 という要素が含まれるかどうかを出力
print(50 in s)
# s から 10 という要素を削除
s.remove(10)
# s に 10 という要素が含まれるかどうかを出力
print(10 in s)
# s の要素を出力
for x in s:
print(x)
出力
True False 40 50 30
set を作る
set()で空のsetを作ることができます。{}だと空のdictになってしまうことに注意してください(dictについてはこの次に説明します)。
{ 要素1, 要素2, 要素3, ... }でsetを作ることができます。set( for 文に入れられるもの )でsetを作ることができます。set([1, 2, 3, 4, 5])などといったように、listからsetを作ることもできます。
{ 内包表記 }でsetを作ることができます。
# 30, 10, 40, 10, 50 という 5 要素から set を作り、s に代入する
# このとき、10 が重複しているので実際には 4 要素の set ができる
s = {30, 10, 40, 10, 50}
# set の中身は順序が保持されるとは限らない
print(s) # {40, 10, 50, 30}
set の要素数
sets に対し、len(s)で s の要素数を取得できます。
# 10 が重複しているので実際には 4 要素の set ができる
s = {30, 10, 40, 10, 50}
print(len(s)) # 4
要素の追加・削除・存在判定
sets と要素 x に対し、s.add(x)で s に要素 x を追加できます。- すでに s に存在する要素を
addしても何も起こりません。
- すでに s に存在する要素を
sets と要素 x に対し、s.remove(x)で s から要素 x を削除できます。- s に存在しない要素を
removeするとエラーになります。 - なお、
s.discard(x)というメソッドもあり、こちらは存在しない要素を指定してもエラーにはなりません(何も起こらない)。
- s に存在しない要素を
sets と要素 x に対し、x in sで s に要素 x が存在するか判定できます(存在するならばTrue、存在しないならばFalseが返る)。- 逆に存在しないか判定したいときは
x not in sと書けます(not (x in s)と同じ)。
- 逆に存在しないか判定したいときは
# 空の set s を作成
s = set()
# s に 100, 200, 300 を追加
s.add(100)
s.add(200)
s.add(300)
print(s) # {200, 100, 300}
# すでに s に存在する要素を add しても何も起こらない
s.add(100)
print(s) # {200, 100, 300}
# s に存在するかどうか判定
print(100 in s) # True
print(404 in s) # False
# s から 100 を削除
s.remove(100)
# s から 100 を削除したので `100 in s` が False になる
print(100 in s) # False
# 存在しない要素を remove するとエラーになるので、条件分岐を挟むとよい
if 404 in s:
s.remove(404)
for 文で要素を列挙する
sets に対し、for x in sで s の要素をひとつずつ x に代入して繰り返し処理を行うことができます。- このとき、要素の列挙される順序は、追加された順や小さい順とは限らないことに注意してください。
s = set()
s.add(100)
s.add(200)
s.add(300)
for x in s:
print(x)
出力
200 100 300
(この出力結果は、実行する環境や Python のバージョンなどで変わる可能性があります)
出力する
sets に対し、print(*s)で s の要素を空白区切りで出力できます。
s = {100, 200, 300}
print(*s)
for x in s:
print(x)
出力
200 100 300 200 100 300
set の注意点
list は set に入れられない
setの要素にはlistを含めることはできません。- これは、
listは整数やstr,tupleなどと違い中身を変更できてしまうからです。 setは中で「ハッシュ値」という値を用いて検索しています。中身が勝手に変わるとこの検索方法が使えないので、Python では「listにはハッシュを定義しない」=「setに入れられない」としています。
- これは、
# エラーにならない
ok = {"Hello", "AtCoder", 123}
# エラーになる
ng = {"Hello", "AtCoder", 123, [4, 5]}
エラー出力
Traceback (most recent call last):
File "/judge/Main.py", line 5, in <module>
ng = {"Hello", "AtCoder", 123, [4, 5]}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: unhashable type: 'list'
(意訳:list はハッシュ化できません)
set を使ったテクニック
list の重複を除去する
- ある
listを一度setに変換してからもう一度listに変換すると、重複が除去できます。- ただし、順序はバラバラになります。
- 特に、
listl の重複を除去しつつソートしたいときはsorted(set(l))などと書くことができます。
l = [30, 10, 40, 10, 50] # 重複を除去する # ただし順序は保持されない l = list(set(l)) print(l) # [40, 10, 50, 30]
l = [30, 10, 40, 10, 50] # 重複を除去しつつソートする l = sorted(set(l)) print(l) # [10, 30, 40, 50]
set のさらなる機能
set 同士の演算
set では和集合、積集合、差集合などのより高度な集合演算もサポートされています。
なお、これらの演算の計算量は集合の大きさに比例することに注意してください。
集合 (set) が返る演算
| 返り値 | 記法(一例) |
|---|---|
| 和集合(s と t の少なくともどちらかに含まれる要素からなる集合) | s | t |
| 積集合(s と t のどちらにも含まれる要素からなる集合) | s & t |
| 差集合(s に含まれて t に含まれない要素からなる集合) | s - t |
| 対称差集合(s と t の片方だけに含まれる要素からなる集合) | s ^ t |
s = {1, 2}
t = {1, 3}
# 和集合(s と t の少なくともどちらかに含まれる要素からなる集合)
print(s | t) # {1, 2, 3}
# 積集合(s と t のどちらにも含まれる要素からなる集合)
print(s & t) # {1}
# 差集合(s に含まれて t に含まれない要素からなる集合)
print(s - t) # {2}
# 対称差集合(s と t の片方だけに含まれる要素からなる集合)
print(s ^ t) # {2, 3}
真偽値が返る演算
| 判定する内容 | 記法(一例) |
|---|---|
| s と t は集合として一致? (s = t) | s == t |
| s と t は集合として異なる? (s \neq t) | s != t |
| s は t の部分集合? (s \subseteq t) | s <= t |
| s は t の真部分集合? (s \subset t) | s < t |
| s は t の上位集合? (s \supseteq t) | s >= t |
| s は t の真上位集合? (s \supset t) | s > t |
dict
dict は連想配列や辞書と呼ばれるデータ構造です。
dict を用いると、「要素 (key) に値 (value) が紐づいている」といったようなデータを簡単に扱うことができます。
set と同様、Python では、要素 (key) に対し「ハッシュ値」というものを定義することで連想配列を実現しています(なので、set と同様、key に list を用いることはできません)。
例えば、以下のようなコードを書くことができます。
# 各人の氏名にテストの点数を紐づけたデータ
d = {"Alice": 100, "Bob": 89, "Charlie": 95}
# データへのアクセス
print(d["Alice"])
print(d["Bob"])
print(d["Charlie"])
# データの追加
d["Dave"] = 77
print(d)
# データの変更
d["Charlie"] = 59
print(d)
# データの削除
d.pop("Bob")
print(d)
# データを順に出力
for name, score in d.items():
print(name, score)
出力
100
89
95
{'Alice': 100, 'Bob': 89, 'Charlie': 95, 'Dave': 77}
{'Alice': 100, 'Bob': 89, 'Charlie': 59, 'Dave': 77}
{'Alice': 100, 'Charlie': 59, 'Dave': 77}
Alice 100
Charlie 59
Dave 77
dict を作る
{}で空のdictを作ることができます。{ key_1: value_1, key_2: value_2, ... }でdictを作ることができます。
# 同じキーがある場合、より後ろに書かれたものが格納される
d = {"Alice": 100, "Bob": 89, "Charlie": 95, "Alice": -1}
print(d) # {'Alice': -1, 'Bob': 89, 'Charlie': 95}
また、内包表記でも { k: v for ... } という形で書くことで dict を作ることができます
square_root = {i * i: i for i in range(5)}
print(square_root) # {0: 0, 1: 1, 4: 2, 9: 3, 16: 4}
dict の要素数
dictd に対し、len(d)で d の要素数を取得できます。
# "Alice" というキーが重複しているので、要素数 3 の dict が作られる
d = {"Alice": 100, "Bob": 89, "Charlie": 95, "Alice": -1}
print(len(d)) # 3
要素の取得・追加・更新・削除・キーの存在判定
dictd とキー k に対し、d[k]でキー k に割り当てられた値を取得できます。- d に含まれないキーを指定するとエラーになります。
dicts とキー k、値 v に対し、d[k] = vでキー k に v を割り当てます。- すでに d に存在するキーに対して行った場合、割り当てる値を更新する操作になります。
dictd とキー k に対し、d.pop(k)で d からキー k を削除できます。- d に存在しないキーを
popするとエラーになります。
- d に存在しないキーを
dictd とキー k に対し、k in dで d にキー k が存在するか判定できます(存在するならばTrue、存在しないならばFalseが返る)。- 逆に存在しないか判定したいときは
k not in dと書けます(not (k in d)と同じ)。
- 逆に存在しないか判定したいときは
# 空の dict d を作成
d = {}
# d に要素を追加
d[200] = "OK"
d[400] = "Bad Request"
d[418] = "I'm a teapot"
print(d[418]) # I'm a teapot
# すでに存在するキーを指定した場合、上書きされる
d[418] = "(Unused)"
print(d[418]) # (Unused)
# d にキーが存在するかどうか判定
print(200 in d) # True
print(404 in d) # False
# あくまでキーとして存在するかなので、値側のみに存在していても False となる
print("OK" in d) # False
d.pop(418)
# d からキー 418 を削除したので `418 in d` が False になる
print(418 in d) # False
# 存在しない要素にアクセスしたり pop したりするとエラーになるので、条件分岐を挟むとよい
if 404 in d:
print(d[404])
d.pop(404)
また、値が存在しないときに初期化をしてくれる defaultdict というものもあり、これを使用すると存在するかどうかの条件分岐を省略することもできます。defaultdict については 4 章で詳しく説明します。
dict を使う場合
l = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]
counter = {}
for x in l:
# counter に要素 x が存在しない状態で counter[x] += 1 とするとエラーになるので、
# if 文で判定し、あらかじめ代入しておく
if x not in counter:
counter[x] = 0
counter[x] += 1
for k, v in counter.items():
print(k, v)
defaultdict を使う場合
from collections import defaultdict
l = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]
counter = defaultdict(int)
for x in l:
# defaultdict の場合エラーにならないので条件分岐が不要
counter[x] += 1
for k, v in counter.items():
print(k, v)
出力(どちらも同じ)
3 2 1 2 4 1 5 2 9 1 2 1 6 1
for 文で要素を列挙する
dictd に対し、for k in d(またはfor k in d.keys())で d のキーを追加された順に k に代入して繰り返し処理を行うことができます。dictd に対し、for v in d.values()で d の値を追加された順に v に代入して繰り返し処理を行うことができます。dictd に対し、for k, v in d.items()で d のキーと値を追加された順にそれぞれ k, v に代入して繰り返し処理を行うことができます。
d = {"Alice": 100, "Bob": 89, "Charlie": 95}
for k in d:
print(k)
for v in d.values():
print(v)
for k, v in d.items():
print(k, v)
出力
Alice Bob Charlie 100 89 95 Alice 100 Bob 89 Charlie 95
問題
リンク先の問題を解いてください。