他言語経験者用 Python 入門② ~コレクション~
コレクション(リスト、タプル、辞書、集合)
複数の値を1つの変数で扱うことのできるオブジェクトをコレクションと呼ぶ。
コレクションには、リスト、タプル、辞書、集合がある。
それぞれの特徴は以下のとおり。
・リスト:
任意の型(整数、浮動小数点数、文字列など)のデータを格納できる。
要素には順序があり、インデックスを用いて要素を指定できる。
リストの要素は変更可能。
他のプログラミング言語における「配列」的な使い方をする。
sample = ['aa', 'bb', 'cc']
・タプル:
任意の型(整数、浮動小数点数、文字列など)のデータを格納できる。
要素には順序があり、インデックスを用いて要素を指定できる。
タプルの要素は変更不可。
sample = ('aa', 'bb', 'cc')
・辞書:
キーと値の組で表されるデータを格納する。
要素は順序を持たない(インデックスでの要素指定ができない)。
他のプログラミング言語における「連想配列」、「マップ」に相当する。
sample = {'key1': 'val1', 'key2': 'val2'}
・集合:
数学でいう「集合」を扱うための型。
各要素は重複することがなく、順序を持たない。
(インデックスでの要素指定ができない)
sample = {'aa', 'bb', 'cc'}
リスト
names = ['taro', 'jiro', 'ichiko']
print(names)
実行結果: ['taro', 'jiro', 'ichiko']
上記の3兄弟に苗字「yamada」を追加してフルネームに変更する。
names = ['taro', 'jiro', 'ichiko']
for name in names:
print(f'yamada {name}')
実行結果:
yamada taro
yamada jiro
yamada ichiko
list関数
list関数を使ってリストを生成できる。
chars = list('Python')
for char in chars:
print(char, end=' ')
実行結果: P y t h o n
nums = list(range(5))
for num in nums:
print(num, end=' ')
実行結果: 0 1 2 3 4
リスト要素の取得・変更
リストの各要素は、角カッコ ([ ]) とインデックスを使って取得・変更できる。
names = ['taro', 'jiro', 'ichiko']
print(names[1])
names[1] = 'saburo'
print(names[1])
実行結果:
jiro
saburo
リストのスライス
以下の方法でリスト要素をスライスできる。
nums = list(range(10))
tmp_nums = nums[2:7]
print(tmp_nums)
実行結果: [2, 3, 4, 5, 6]
リスト要素の追加
append メソッドを使用して、リスト要素が追加できる。
names = ['taro', 'jiro', 'ichiko']
names.append('niko')
print(names)
実行結果: ['taro', 'jiro', 'ichiko', 'niko']
リストの結合
リスト同士であれば、プラス演算子を使ってリストとリストを結合できる。
※ 文字列とリスト、などのようにリスト以外とは結合できないので注意。
names1 = ['taro', 'jiro', 'saburo']
names2 = ['ichiko', 'niko', 'miko']
names = names1 + names2
print(names)
実行結果: ['taro', 'jiro', 'saburo', 'ichiko', 'niko', 'miko']
リスト要素の削除
リスト要素の削除には、del 文と remove メソッドを使う。
まずは del 文の使い方からみていく。
names = ['taro', 'jiro', 'saburo']
del names[1]
print(names)
実行結果: ['taro', 'saburo']
del 文は1要素の削除だけではなく、リスト自体の削除もできる。
names = ['taro', 'jiro', 'saburo']
del names
print(names)
実行結果: NameError: name 'names' is not defined
次に remove メソッド。
これは前述の append メソッドと対になる。
以下のように、del とは異なりインデックスではなく要素そのものを指定して削除する。
※ リストに同じ値が複数ある場合、インデックスの小さい方が削除される。
names = ['taro', 'jiro', 'saburo']
names.remove('jiro')
print(names)
実行結果: ['taro', 'saburo']
リスト内包表記
リストを少ない行数で作成するための1記法。
練習のため、0~9までの数字リストを list 関数を使わず作成してみる。
num_list = [num for num in range(10)]
print(num_list)
実行結果: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
練習②として、上記リストを偶数だけで作成する。
num_list = [num for num in range(10) if num % 2 == 0]
print(num_list)
実行結果: [0, 2, 4, 6, 8]
練習③として、さらに複雑なネスト構造を掛け算九九をサンプルに作成する。
for row in [[x * y for y in range(1, 10)] for x in range(1, 10)]:
print(row)
実行結果:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[2, 4, 6, 8, 10, 12, 14, 16, 18]
[3, 6, 9, 12, 15, 18, 21, 24, 27]
[4, 8, 12, 16, 20, 24, 28, 32, 36]
[5, 10, 15, 20, 25, 30, 35, 40, 45]
[6, 12, 18, 24, 30, 36, 42, 48, 54]
[7, 14, 21, 28, 35, 42, 49, 56, 63]
[8, 16, 24, 32, 40, 48, 56, 64, 72]
[9, 18, 27, 36, 45, 54, 63, 72, 81]
練習④として、掛け算九九の結果を2桁表示にして表を綺麗にする。
for row in [[str(x * y).zfill(2) for y in range(1, 10)] for x in range(1, 10)]:
print(row)
実行結果:
['01', '02', '03', '04', '05', '06', '07', '08', '09']
['02', '04', '06', '08', '10', '12', '14', '16', '18']
['03', '06', '09', '12', '15', '18', '21', '24', '27']
['04', '08', '12', '16', '20', '24', '28', '32', '36']
['05', '10', '15', '20', '25', '30', '35', '40', '45']
['06', '12', '18', '24', '30', '36', '42', '48', '54']
['07', '14', '21', '28', '35', '42', '49', '56', '63']
['08', '16', '24', '32', '40', '48', '56', '64', '72']
['09', '18', '27', '36', '45', '54', '63', '72', '81']
リスト操作用の関数
以下に一覧で掲載する。
補足が必要なものは後述する。
(*1) index メソッドの戻り値
引数に指定した「item」が複数含まれている場合、戻り値として最小のインデックスが返される。
「start」と「end」にはリスト内で検索を行う範囲を指定する(インデックスstart~インデックスend-1の範囲が検索対象となる)。
指定した範囲(デフォルトはリストの全要素)に、指定した値が含まれていないときにはエラー(ValueError例外)が発生する。
(*2) pop メソッドの戻り値
引数を指定しなかった場合には、末尾の要素が削除され、その値が戻り値となる。
一覧に載せていない重要な操作
【map 関数】
リストの各要素に対し何かしら処理を加え、その結果を要素とするイテレータを戻り値とする。
map(function, iterable, ……)
names = ['taro', 'jiro', 'ichiko']
full_names = map(lambda x: 'yamada ' + x, names)
print(list(full_names))
実行結果: ['yamada taro', 'yamada jiro', 'yamada ichiko']
【filter 関数】
map 関数と同様にリストに何らかの処理を適用して、その結果を基に新たなイテレータを作成する。
map との違いは、第1引数には真偽値を返す関数を渡し、その条件に合致する要素で構成されたイテレータが戻り値となること。
nums = list(range(10))
odd_nums = filter(lambda x: x % 2 != 0, nums)
print(list(odd_nums))
実行結果: [1, 3, 5, 7, 9]
【all 関数、any 関数】
all(iterable)
any(iterable)
all 関数は、引数に渡した反復可能オブジェクトの全要素が「真」と判定された場合にTrueを返す(いずれかの要素が「偽」と判定された場合にはFalseを返す)。
any 関数は、引数に渡した反復可能オブジェクトのいずれかの要素が真と判定された場合にTrueを返す(全ての要素が「偽」と判定されたらFalseを返す)。
※ 空の反復可能オブジェクトを渡した場合には、all 関数はTrueを、any 関数はFalseを戻り値とする。
その他の操作(enumerate、zip)
enumerate 関数は、反復可能(イテラブル)なオブジェクトから、インデックスと値から成るタプルを生成する。
names = ['taro', 'jiro', 'ichiko']
for i, v in enumerate(names):
print(f'{i}: {v}')
実行結果:
0: taro
1: jiro
2: ichiko
zip 関数は複数の反復可能オブジェクトを、同一インデックスとして統合できる。説明ではわかりづらいので実例で示す。
names = ['taro', 'jiro', 'ichiko']
points = [89, 57, 90]
for v1, v2 in zip(names, points):
print(f'{v1}: {v2}')
実行結果:
taro: 89
jiro: 57
ichiko: 90
タプル
タプルはリストと非常によく似ている。
・角カッコ「[ ] 」の代わりに丸カッコ「( )」で囲む。
・丸カッコは省略可能(空のタプル作成時は省略不可)。
・リストはミュータブル、タプルはイミュータブル。
sample = ('taro', 'yamada', 20)
print(sample)
実行結果: ('taro', 'yamada', 20)
tuple 関数を使用してタプルを生成できる。
tpl = tuple(['aaa', 'bbb', 20])
print(tpl)
実行結果: ('aaa', 'bbb', 20)
タプルのパック・アンパック
tpl = 'taro', 'yamada', 20 # タプルのパック
print(tpl)
first_name, family_name, age = tpl # タプルのアンパック
print(f'{first_name} {family_name} {age}歳')
実行結果:
('taro', 'yamada', 20)
taro yamada 20歳
タプルの操作関数一覧
下表の例では、変数mytupleに「('taro', 'yamada', 20)」というタプルが代入されている前提とする。
辞書
キーと値を1データとして、複数格納することができるオブジェクト。
d = {'key1':'val1', 'key2':'val2', 'key3':'val3'}
print(d)
実行結果: {'key1': 'val1', 'key2': 'val2', 'key3': 'val3'}
dict 関数を使用して辞書を生成できる。
mydict = dict() # 空の辞書
print(mydict)
mydict = dict(hoge='HOGE', fuga='FUGA') # キーワード引数による辞書
print(mydict)
mydict = dict([('hoge', 1), ['fuga', 2]]) # 反復可能オブジェクトによる辞書
print(mydict)
mydict = dict({'hoge': 'HOGE', 'fuga': 'FUGA'}, piyo='PIYO') # 組み合わせ
print(mydict)
実行結果:
{}
{'hoge': 'HOGE', 'fuga': 'FUGA'}
{'hoge': 1, 'fuga': 2}
{'hoge': 'HOGE', 'fuga': 'FUGA', 'piyo': 'PIYO'}
最後のサンプルは、辞書とキーワード引数の両方を組み合わせて渡している。
反復可能オブジェクトとキーワード引数も渡すことができる。
ただし、辞書、反復可能オブジェクト、キーワード引数の3種類を同時に渡すことはできない。
辞書の内包表記
{キーの算出式: その値を算出する式 for ループ変数 in 反復可能オブジェクト}
subjects = ['english', 'mathematics', 'history']
names = ['taro', 'jiro', 'ichiko']
points = [80, 65, 90]
grade = {s: {'name': n, 'point': p} for s, n, p in zip(subjects, names, points)}
for key, val in grade.items():
print(f'Subject: {key}, Name: {val["name"]}, Point: {val["point"]}')
実行結果:
Subject: english, Name: taro, Point: 80
Subject: mathematics, Name: jiro, Point: 65
Subject: history, Name: ichiko, Point: 90
辞書の値の取得
リストやタプル同様、角カッコ「[ ]」によっても取得できるが、get メソッドを使って取得できる。
sample = {'A': 'taro', 'B': 'jiro', 'C': 'ichiko'}
print(sample.get('B'))
print(sample.get('D'))
実行結果:
jiro
None
角カッコでの取得とは異なり、 getメソッドの場合は存在しないキーを指定してもエラーにはならない。
辞書の操作
下表の例では、変数mydictに「{'hoge': 'HOGE', 'fuga': 'FUGA', 'piyo': 'PIYO'}」という辞書が代入されている前提とする。
辞書に特化した操作
・update メソッド
updateメソッドは、辞書に存在する項目を変更したり、辞書にない項目を追加したりといった操作ができる。
update([iterable][, **kw_args])
第1引数の iterable は省略可能。
iterableが辞書の場合は、辞書にその項目の内容が反映される(既存のキーについてはその値が変更され、新規のキーならその項目が追加される)。
他の反復可能オブジェクトについては、その要素が「2つの要素を持つ反復可能オブジェクト」である必要がある。
その場合、最初の要素がキーに、次の要素がその値に見なされて、辞書の内容が更新される。
第2引数の kw_args には、「キー=値」形式のキーワード引数を任意の数だけ指定できる。
この場合、「キー」が辞書のキーとして、「値」がその値として使われて、辞書の内容が更新される
・pop メソッド
popメソッドは、指定したキーに関連付けられている値を得ると共に、そのキーと値の組を辞書から削除する。
pop(key[, default])
辞書のループ処理
sample = {'A': 'taro', 'B': 'jiro', 'C': 'ichiko'}
for item in sample:
print(item, end=' ')
実行結果: A B C
実行結果のとおり、辞書を for ループし、上記のように処理した場合は、各キーが取得される。
それぞれの値を取得したい場合、以下のようにすると良い。
sample = {'A': 'taro', 'B': 'jiro', 'C': 'ichiko'}
for item in sample:
print(sample[item], end=' ')
実行結果: taro jiro ichiko
さらに良い方法がある。
keys, values, items というメソッドを使うと、各キー/値を簡単に扱うことができる。
sample = {'A': 'taro', 'B': 'jiro', 'C': 'ichiko'}
for key, value in sample.items():
print(key, value, end=' ')
実行結果: A taro B jiro C ichiko
sample = {'A': 'taro', 'B': 'jiro', 'C': 'ichiko'}
for key in sample.keys():
print(key, end=' ')
実行結果: A B C
sample = {'A': 'taro', 'B': 'jiro', 'C': 'ichiko'}
for value in sample.values():
print(value, end=' ')
実行結果: taro jiro ichiko
集合
集合の特徴は、各要素が重複しないことと、順序を持たないこと。
langs = {'Java', 'Javascript', 'Python', 'Java'}
print(langs)
実行結果: {'Python', 'Java', 'Javascript'}
set 関数
set([iterable])
集合の内包表記
{集合の要素を算出する式 for ループ変数 in 反復可能オブジェクト}
# 0~9までの集合
nums = {x for x in range(10)}
print(nums)
実行結果: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
# 0~9までの中で偶数のみの集合
nums = {x for x in range(10) if x % 2 == 0}
print(nums)
実行結果: {0, 2, 4, 6, 8}
集合の操作
集合に要素を追加するには、addメソッドを使用する。
集合オブジェクト.add(item)
集合から指定の要素を削除するには、removeメソッドあるいはdiscardメソッドを使用する。
集合内での要素の有無を気にすることなく、要素の削除を行いたければdiscardメソッドを、要素がないときには何か別の処理を行う必要があればremoveメソッドを呼び出す。あるいはin演算子を使って要素の存在確認を行った上でremoveメソッドを呼び出すことも考えられる。
集合オブジェクト.remove(item)
集合オブジェクト.discard(item)
集合から任意の要素を削除するには、popメソッドを使用する。
集合オブジェクト.pop()
その他の操作一覧
以下の表では変数mysetに集合「{1, 2, 3}」が代入されているものとする。
集合の比較
myset1 = {1, 2, 3}
myset2 = {3, 2, 1}
myset3 = {1, 2, 3, 4}
print(myset1 == myset2) # True
print(myset2 == myset3) # False
print(myset1 != myset3) # True
実行結果:
True
False
True
集合の包含関係(部分集合/上位集合)
集合Aと集合Bがあったとき、集合Aの全要素を集合Bも持っている場合、集合Aは集合Bの「部分集合」、集合Bは集合Aの「上位集合」と呼ぶ。
集合Aにはない要素を集合Bが持っていれば、集合Aは集合Bの「真部分集合」、集合Bは集合Aの「真上位集合」と呼ぶ。
(集合Aが持つ全要素を集合Bが持っており、集合Bが持つ全要素を集合Aが持っている、つまり2つの集合が等しい場合には、一方がもう一方の部分集合かつ上位集合ともなる)。
・部分集合を調べる
issubset メソッド
<= 演算子
・真部分集合を調べる
< 演算子
・上位集合を調べる
issuperset メソッド
>= 演算子
・真上位集合を調べる
> 演算子
集合1.issubset(集合2)
集合1.issuperset(集合2)
集合の和/差/積/対称差
和:複数の集合に含まれる全ての要素で構成される集合を求める
差:ある集合に含まれるが、他の集合には含まれないものを要素とする集合を求める
積:複数の集合に共通する要素で構成される集合を求める
対称差それぞれの集合のみに存在する要素で構成される集合を求める
それぞれを求めるメソッド・演算子は以下のとおり。
和: unionメソッド、「|」演算子
差: differenceメソッド、「-」演算子
積: intersectionメソッド、「&」演算子
対象差: symmetric_differenceメソッド、「^」演算子
使い方のサンプルプログラムを掲載する。
# 和を求める
myset1 = {1, 2, 3}
myset2 = {2, 3, 4, 5}
print(myset1.union(myset2)) # union メソッドを使用
print(myset1 | myset2) # | 演算子を使用
実行結果:
{1, 2, 3, 4, 5}
{1, 2, 3, 4, 5}
# 差を求める
myset1 = {1, 2, 3}
myset2 = {2, 3, 4, 5}
print(myset1.difference(myset2)) # difference メソッドを使用
print(myset1 - myset2) # - 演算子を使用
実行結果:
{1}
{1}
# 積を求める
myset1 = {1, 2, 3}
myset2 = {2, 3, 4, 5}
print(myset1.intersection(myset2)) # intersection メソッドを使用
print(myset1 & myset2) # & 演算子を使用
実行結果:
{2, 3}
{2, 3}
# 対象差を求める
myset1 = {1, 2, 3}
myset2 = {2, 3, 4, 5}
print(myset1.symmetric_difference(myset2))
print(myset1 ^ myset2)
実行結果:
{1, 4, 5}
{1, 4, 5}