ネイティブデータ型

飛ぶこむ

Pythonではすべての値がデータ型を持っているにもかかわらず、 変数のデータ型を宣言する必要はない。

これはどういう仕組みで動いているんだろう? Python は、それぞれの変数について、 もともとの代入がなされたときに何の型であるかを把握し、 それを内部的に追跡している。

Pythonは数多くのネイティブデータ型を持っている。 重要なものは以下。

  • 「ブール値」 は、TrueまたはFalseのどちらかを表す。
  • 「数値」 は、整数(1や2)を表したり、浮動小数点数(1.1や1.2)を表したり、分数(1/2や2/3)を表したりできる。複素数さえも表せる。
  • 「文字列」 はUnicode文字のシーケンスだ。例: htmlドキュメント。
  • 「バイト列とバイト配列」。例: jpeg画像。
  • 「リスト」 は、値のシーケンスだ。要素は順序づけされる。
  • 「タプル」 は、値のイミュータブルなシーケンスだ。要素は順序づけされる。
  • 「集合」 は、値を詰めた袋だ。要素は順序づけされない。
  • 「辞書」 は、キーと値のペアを詰めた袋だ。要素は順序づけされない。

Pythonではあらゆるものがオブジェクトなので、モジュール・関数・クラス・メソッド・ファイル、そしてコンパイルされたコードといった型すら存在する。

「モジュール」は、名前を持つ( .__name__ )、 関数は .docstrings() を持つ、

ブール値

ブール値は真または偽のどちらかを表す。 Pythonは、True, Falseと分かりやすく命名された2つの定数を持っており、これらはブール値を直接代入するために使える。

式を評価した結果もブール値になりうる。 特定の箇所(たとえばif文)では、式を評価した結果がブール値になることが要求される場合もある。 このような箇所は、 ブール値のコンテクスト と呼ばれる。 そこでは事実上どんな式でも使うことができ、 Python は、その式の真偽値を決定しようと試みる。

例として、 変数size は整数で、0も整数、 そして< は数値演算子だ。 式size < 0の結果は常にブール値になる。

In [1]:
size = 1
size < 0
Out[1]:
False
In [2]:
size = 0
size < 0
Out[2]:
False
In [3]:
size = -1
size < 0
Out[3]:
True

Python 2から残されたいくつかの古い問題のために、ブール値を数値として扱うこともできる。

  • Trueは1
  • Falseは0。
In [4]:
True + True
Out[4]:
2
In [5]:
True - False
Out[5]:
1
In [6]:
True * False
Out[6]:
0
In [7]:
True / False
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-7-dcacc0b7b97e> in <module>()
----> 1 True / False

ZeroDivisionError: division by zero

数値

Pythonは、整数と浮動小数点数の両方をサポートしている。この2つを区別するための型宣言は存在せず、Pythonは小数点が存在するかしないかで両者を区別する。

値や変数の型を調べるにはtype()関数 が使える

In [8]:
type(1)
Out[8]:
int

値や変数の型が特定の型かどうかを調べるには、 isinstane()関数 が使える。

In [9]:
isinstance(1, int)
Out[9]:
True
In [10]:
1 + 1
Out[10]:
2

intをfloatに加えるとfloat になる。 Pythonはintをfloatに型強制 (coercion) してから加算を行い、 その結果としてfloatを返す。

In [11]:
1 + 1.0
Out[11]:
2.0
In [12]:
type(2.0)
Out[12]:
float

整数から浮動小数点数への型強制とその逆

いくつかの演算子(加算など)は必要に応じて整数を浮動小数点数に型強制する。自分でこの型強制を行うことも可能。

float()関数 を呼び出すことで、明示的にintをfloat へ型強制できる。

In [13]:
float(2)
Out[13]:
2.0

int()関数 を呼び出すことで、明示的にfloatをintに型強制できる。

In [14]:
int(2.0)
Out[14]:
2

int()関数 は、切り捨てを行う。

In [15]:
int(2.5)
Out[15]:
2

int()関数は、負数を0の方向へ向けて切り捨てる。 これは正しい切り捨て関数で、床関数 (floor function) ではない。

In [16]:
int(-2.5)
Out[16]:
-2

浮動小数点数は小数第15位まで正確。

In [17]:
1.12345678901234567890
Out[17]:
1.1234567890123457
In [18]:
type(1000000000000000)
Out[18]:
int
  • Python 2はintとlongを区別していた。intデータ型が表現できる数はsys.maxintに制限されていて、この値はプラットフォームごとに異なるが通常は232-1だった。
  • Python 3はただ一つの整数型を持っており、これはPython2のlong型とほぼ同様に振る舞う。 詳細はpep 237

python3 ではsys.maxintmaxsizeに置き換えられている。

In [19]:
import sys
sys.maxsize
Out[19]:
9223372036854775807

一般的な数値演算

In [20]:
11 / 2
Out[20]:
5.5

結果が正の数の場合は、整数への切り捨て(四捨五入ではない)とみなすことができるが、これについては注意が必要。

In [21]:
11 // 2
Out[21]:
5

負の数を整数除算する場合、//演算子は最も近い整数に繰り「上げる」。数学的に言えば、−6は−5より小さいので「繰り下げ」というべきだが、−5に切り捨てられることを期待していると足もとをすくわれる。

In [22]:
-11 // 2 
Out[22]:
-6

//演算子は常に整数を返すわけではない。 分子や分母のどちらか一方でもfloatの場合は、 なお結果を最も近い整数に丸めてくれるのだが、 実際の戻り値はfloat で返される。

In [23]:
11.0 // 2 
Out[23]:
5.0

**演算子は「べき乗」を意味する。 つまり$11^2 = 121$。

In [24]:
11 ** 2
Out[24]:
121

%演算子は整数除算の余りを返す。 11を2で割ると5余り1になるので、ここでの結果は1となる。

In [25]:
11 % 2
Out[25]:
1

Python 3では、 /演算子 は常に浮動小数点数の除算を行う。 詳細はpep 238

分数

分数も扱える。

分数を使うために、fractionsモジュールをインポート。

In [26]:
import fractions
fractions
Out[26]:
<module 'fractions' from 'C:\\Miniconda3\\lib\\fractions.py'>

分数を定義するには、Fractionオブジェクトを作って分子と分母を渡す。

In [27]:
x = fractions.Fraction(1, 3)
x
Out[27]:
Fraction(1, 3)

分数をつかった普通の数学的演算ならなんでもできる。 演算結果は新しいFractionオブジェクトとして返される。

$$ 2 * \frac{1}{3} = \frac{2}{3} $$
In [28]:
x * 2
Out[28]:
Fraction(2, 3)

Fractionオブジェクトは自動的に約分される。

$$ \frac{6}{4} = \frac{3}{2} $$
In [29]:
fractions.Fraction(6, 4)
Out[29]:
Fraction(3, 2)

Pythonは、分母がゼロの分数が作成されないようにうまくやってくれる。 zero除算エラーが出る。

In [30]:
fractions.Fraction(0, 0)
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-30-b2c38c7f647e> in <module>()
----> 1 fractions.Fraction(0, 0)

C:\Miniconda3\lib\fractions.py in __new__(cls, numerator, denominator, _normalize)
    184 
    185         if denominator == 0:
--> 186             raise ZeroDivisionError('Fraction(%s, 0)' % numerator)
    187         if _normalize:
    188             if type(numerator) is int is type(denominator):

ZeroDivisionError: Fraction(0, 0)

三角法

Pythonでは基本的な三角法も行える。

math は、円周率$\pi$ のために定数を用意している。

In [31]:
import math
math
Out[31]:
<module 'math' (built-in)>
In [32]:
math.pi
Out[32]:
3.141592653589793

math には基本的な三角関数が全部入っている。 例えば$\sin()$, $\cos()$, $\tan()$ といったものや、 $\arcsin()$ のような派生形も入っている。

In [33]:
 math.sin(math.pi / 2)
Out[33]:
1.0

Pythonは無限の精度を持たないことに注意。 $\tan(\frac{\pi}{4})$ は、 1.0を返すべきなのに、0.99999999999999989が返ってきてしまう。

In [34]:
math.tan(math.pi / 4)
Out[34]:
0.9999999999999999

bool値のコンテキストでの数値

数値はif文のようなブール値のコンテクストで使うことができる。

  • ゼロは偽であり、
  • ゼロではない数値は真。
In [35]:
def is_it_true(anything):
    if anything:
        print("yes, it's true")
    else:
        print("no, it's false")
In [36]:
is_it_true(1)
yes, it's true
In [37]:
is_it_true(-1)
yes, it's true
In [38]:
is_it_true(0)
no, it's false

ゼロでない浮動小数点数は真であり、0.0は偽。 これには注意しなければならない。 わずかな丸め誤差が発生した場合(前の節で見たように、これは起こりうる)、Pythonは0ではなく0.0000000000001を評価することになり、 その結果Trueが返されてしまう。

In [39]:
is_it_true(0.1)
yes, it's true
In [40]:
is_it_true(0.0)
no, it's false

分数もブール値のコンテクストで使うことができる。 Fraction(0, n) はすべての n について偽になる。 それ以外の分数は真。

In [41]:
is_it_true(fractions.Fraction(1, 2))
yes, it's true
In [42]:
is_it_true(fractions.Fraction(0, 1))
no, it's false

リスト

PythonのリストはJavaの配列を遙かに超えたもの。 ArrayListクラスに似ている。 これは任意のオブジェクトを格納できるし、 新しい要素を追加するとサイズが動的に拡張される。

リストを作る

カンマで区切って並べた値を、角括弧で包む。

要素が元の順番を保持していることに注意。 リストは順序づけられた要素の集合。

In [43]:
a_list = ['a', 'b', 'mpilgrim', 'z', 'example']
a_list
Out[43]:
['a', 'b', 'mpilgrim', 'z', 'example']

リストはインデックスが0から始まる配列のように扱うことができる。 空でないリストの先頭の要素は常にa_list[0] 。

In [44]:
a_list[0]
Out[44]:
'a'
In [45]:
a_list[4]
Out[45]:
'example'

負のインデックスは、配列の後ろ側から逆順に数えて要素にアクセスする。 空でないリストの最後の要素は常にa_list[-1]になる。

In [46]:
a_list[-1]
Out[46]:
'example'

負のインデックスが理解しにくいのなら: a_list[-n] == a_list[len(a_list) - n]。 つまりこのリストでは、a_list[-3] == a_list[5 - 3] == a_list[2]

In [47]:
a_list[-3]
Out[47]:
'mpilgrim'

リストをスライスする

リストを定義したら、そのリストの任意の一部分を新しいリストとして取得できる。これはリストのスライスという。

In [48]:
a_list
Out[48]:
['a', 'b', 'mpilgrim', 'z', 'example']

戻り値は、

  1. 1つ目のスライスインデックス(この例ではa_list[1])から、
  2. 2つ目のスライスインデックス(この例ではa_list[3])の直前までを含む新しいリストで、順序も保持されている。
In [49]:
a_list[1:3]
Out[49]:
['b', 'mpilgrim']

スライスは、スライスインデックスの一方または両方が負の数でも動作する。 これは次のように考えると良いかもしれない: リストを左から右へ読み、

  1. 1つ目のスライスインデックスで欲しい最初の要素を指定し、
  2. 2つ目のスライスインデックスは欲しくない最初の要素を指定する。戻り値はその間に含まれるすべて。
In [50]:
a_list[1:-1]
Out[50]:
['b', 'mpilgrim', 'z']
In [51]:
a_list[0:3]
Out[51]:
['a', 'b', 'mpilgrim']

左側のスライスインデックスが0のときは、これを省略できる。 つまり、a_list[:3] は、始点のインデックスが暗黙に0 とみなされるので、a_list[0:3] と同じ。

In [52]:
a_list[:3]
Out[52]:
['a', 'b', 'mpilgrim']

右側のスライスインデックスがリストの長さと同じである場合は、これを省略できる。つまりa_list[3:]は、このリストが5つの要素から構成されているので、a_string[3:5]と同じになる

この5つの要素を持つリストでは、 a_list[:3] は最初の3要素を返し、 a_string[3:]は最後の2要素を返す。 実際に、リストの長さにかかわらず、 a_list[:n] は常に最初の n 個の要素を返し、 a_list[n:] はその残りを返す。

In [53]:
a_list[3:]
Out[53]:
['z', 'example']

両方のスライスインデックスが省略された場合は、リストのすべての要素が含まれる。しかし、これは元の a_list 変数とは異なる。これは、たまたますべて同じ要素を持った新しいリストなのだ。だから、a_list[:]はリストの完全なコピーを作るための簡易な方法として使える。

In [54]:
a_list[:]
Out[54]:
['a', 'b', 'mpilgrim', 'z', 'example']
In [55]:
a_list[:6]
Out[55]:
['a', 'b', 'mpilgrim', 'z', 'example']

要素をリストに追加する

要素をリストに追加する方法は4つある。

In [56]:
a_list = ['a']
a_list
Out[56]:
['a']

+演算子はリストを結合して新たなリストを作る。

メモリの使用量が問題となるような場合には、このリストの結合によって新たなリストがメモリ上に作成されることに注意すること。 この場合、その新しいリストはすぐに既存の変数a_listへ代入される。 つまり、この行は実際には

  • 結合と
  • 代入

という2つのステップから成り立っており、巨大なリストを扱うときには(一時的に)大量のメモリを消費する可能性がある。

リストは任意のデータ型の要素を含むことができ、1つのリストの要素がすべて同じ型である必要はない。

In [57]:
a_list = a_list + [2.0, 3]
a_list
Out[57]:
['a', 2.0, 3]

append()メソッドはリストの末尾に要素を1つ追加。

In [58]:
a_list.append(True)
a_list
Out[58]:
['a', 2.0, 3, True]

extend()メソッドは1つの引数としてリストをとり、 引数として与えられたリストの各要素を元のリストへ追加する。

In [59]:
a_list.extend(['four', 'Ω'])
a_list
Out[59]:
['a', 2.0, 3, True, 'four', 'Ω']

insert()メソッドは、1つの要素をリストに挿入する。 1つ目の引数は、挿入によって「位置がずらされる最初の要素のインデックス」。

In [60]:
a_list.insert(0, 'Ω')
a_list
Out[60]:
['Ω', 'a', 2.0, 3, True, 'four', 'Ω']

append()とextend()の違い

In [61]:
a_list = ['a', 'b', 'c']
a_list
Out[61]:
['a', 'b', 'c']

extend()メソッドは、 単一の引数として常にリストを取り、そのリストの各要素をa_listへ追加する。

In [62]:
a_list.extend(['d', 'e', 'f'])
a_list
Out[62]:
['a', 'b', 'c', 'd', 'e', 'f']
In [63]:
len(a_list)
Out[63]:
6
In [64]:
a_list[-1]
Out[64]:
'f'

append()メソッドも引数を1つだけとるが、その引数はどんなデータ型でもいい。

In [65]:
a_list.append(['g', 'h', 'i'])
a_list
Out[65]:
['a', 'b', 'c', 'd', 'e', 'f', ['g', 'h', 'i']]

6つの要素を持つリストに対してリストを加える (append) と、 結果は…… 7つの要素を持つリストになる。

In [66]:
len(a_list)
Out[66]:
7

なぜなら最後の要素(今加えたもの)はリスト自体のため。 リストはどんなデータ型でも含むことができるので、リストが他のリストを含むこともできる。これは望む結果かもしれないし、そうでないかもしれない。

In [67]:
a_list[-1]
Out[67]:
['g', 'h', 'i']

リストの値を検索する

In [68]:
a_list = ['a', 'b', 'new', 'mpilgrim', 'new']
a_list
Out[68]:
['a', 'b', 'new', 'mpilgrim', 'new']

count()メソッドは指定された値がリストの中に何回出現するかを数える。

In [69]:
a_list.count('new')
Out[69]:
2

リストの中に特定の値があるかどうかを知りたいだけの場合は、 count()メソッドよりもin演算子の方がわずかながら処理が早い。 リスト中にその値が何回現われるのかは教えないため。 in演算子は、常にTrueまたはFalseを返す。

In [70]:
'new' in a_list
Out[70]:
True
In [71]:
'c' in a_list
Out[71]:
False

値がリストのどこにあるのかを知る必要がある場合は、 index()メソッドを呼び出だす。 デフォルトでは、このメソッドはリスト全体を検索するが、

  • 2番目の引数で検索を開始するインデックス(0を起点とする)を指定すること
  • さらに3つ目の引数で検索を止めるインデックス(0を起点とする)を指定することもできる。
In [72]:
a_list.index('mpilgrim')
Out[72]:
3
In [73]:
a_list.index('new')
Out[73]:
2

値がリストから見つからない場合、 index()メソッドは例外を送出する。

In [74]:
a_list.index('c')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-74-4e77f8c6f6f7> in <module>()
----> 1 a_list.index('c')

ValueError: 'c' is not in list

index()メソッドは、値をリストから見つけ出せなかった場合に例外を送出する。 これは他の多くの言語と際だって異なる点。 他の多くの言語はここで無効なインデックス(例えば-1)を返す場合が多い。 こうすれば、後の方で密やかに奇妙な振る舞いを引き起こすのではなく、 問題の根源でクラッシュしてくれる。

Pythonでは、そもそも「-1」がリストの有効なインデックスになる。

リストから要素を取り除く

リストから要素を取り除くのにもいくつかの方法がある。

In [75]:
a_list = ['a', 'b', 'new', 'mpilgrim', 'new']
a_list[1]
Out[75]:
'b'

特定の要素をリストから削除するには、del文が使える。

In [76]:
del a_list[1]
a_list
Out[76]:
['a', 'new', 'mpilgrim', 'new']
In [77]:
a_list[1]
Out[77]:
'new'

インデックスの位置が分からないときは、 代わりに値を指定することで要素を取り除くことができる。

remove()メソッドでもリストから要素を取り除ける。 remove()メソッドは値を受け取り、それで最初に見つかったものをリストから取り除く。

In [78]:
a_list.remove('new')
a_list
Out[78]:
['a', 'mpilgrim', 'new']
In [79]:
a_list.remove('new')
a_list
Out[79]:
['a', 'mpilgrim']

remove()メソッドは好きなだけ呼び出すことができるが、 リストの中に存在しない値を取り除こうとすると、このメソッドは例外を出す。

In [80]:
a_list.remove('new')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-80-c2064dc3d02e> in <module>()
----> 1 a_list.remove('new')

ValueError: list.remove(x): x not in list

リストから要素を取り除く: おまけ

pop()メソッドは、リストから要素を取り除くもう一つの方法。

リストのpop()メソッドを引数なしに呼び出すと、 リストの最後の要素が削除されて、削除された値が返される。

In [81]:
a_list = ['a', 'b', 'new', 'mpilgrim']
a_list.pop()
Out[81]:
'mpilgrim'
In [82]:
a_list
Out[82]:
['a', 'b', 'new']

任意の要素をリストからpopできる。これにはインデックスをpop()メソッドに渡すだけ。

In [83]:
a_list.pop(1)
Out[83]:
'b'
In [84]:
a_list
Out[84]:
['a', 'new']
In [85]:
a_list.pop()
Out[85]:
'new'
In [86]:
a_list.pop()
Out[86]:
'a'

空リストのpop()メソッドを呼び出すと、例外が出される。

In [87]:
a_list.pop()
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-87-3bc9ba441032> in <module>()
----> 1 a_list.pop()

IndexError: pop from empty list

pushの代わりが.append() になる

ブール値のコンテクストでのリスト

リストはif文のような ブール値のコンテクストでも使うことができる。

ブール値のコンテクストでは、空のリストは偽。

In [88]:
is_it_true([])
no, it's false

要素を1つでも持つリストは真。

In [89]:
is_it_true(['a'])
yes, it's true
In [90]:
is_it_true([False])
yes, it's true

タプル

タプルはイミュータブルなリスト。 いったん作成されたタプルは、どんな手段によっても変更できない。

タプルはリストと同じような方法で定義するが、 要素の集合の全体を角括弧ではなく丸括弧「()」で包む点が異なる。

In [91]:
a_tuple = ("a", "b", "mpilgrim", "z", "example")
a_tuple
Out[91]:
('a', 'b', 'mpilgrim', 'z', 'example')
In [92]:
a_tuple[0]
Out[92]:
'a'

リストと同様に、負のインデックスはタプルの後ろ側から数えられる。

In [93]:
a_tuple[-1]
Out[93]:
'example'

リストと同様に、スライスすることもできる。リストをスライスすると新しいリストが得られるように、タプルをスライスすると新しいタプルが得られる。

In [94]:
a_tuple[1:3]
Out[94]:
('b', 'mpilgrim')

タプルとリストの大きな違いは、タプルは変更ができないという点だ。専門用語ではこれを、タプルは イミュータブル (immutable) であると言う。

リストはappend(), extend(), insert(), remove(), pop() などのメソッドを持っているが、タプルはこれらのメソッドを持っていない。

In [95]:
a_tuple
Out[95]:
('a', 'b', 'mpilgrim', 'z', 'example')
In [96]:
a_tuple.append("new")
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-96-1b46e9a44545> in <module>()
----> 1 a_tuple.append("new")

AttributeError: 'tuple' object has no attribute 'append'
In [97]:
a_tuple.remove("z")
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-97-e1d2acaf7dc7> in <module>()
----> 1 a_tuple.remove("z")

AttributeError: 'tuple' object has no attribute 'remove'

タプル中の要素を検索することはできる。

In [98]:
a_tuple.index("example")
Out[98]:
4

in演算子を使って、要素がタプルに含まれているかどうかを調べることもできる。

In [99]:
"z" in a_tuple
Out[99]:
True

タプルの利点

  • タプルはリストよりも高速。変更を加える予定のない集合を定めて、それをイテレートするだけのつもりなら、リストの代わりにタプルを使うとよい。

  • 変更の必要がないデータを「書き込み保護」すれば、コードはもっと安全なものになる。タプルをリストの代わりに使うことは、データが不変であることを示す暗黙的なassert文があるようなものであり、それを上書きするには特別の意図(と特定の関数)が要求される。

  • ある種のタプル(具体的に言えば、文字列とか数字とか他のタプルとかいったようなイミュータブルな値からなるタプル)は辞書のキーとして使える。リストはイミュータブルではないので、辞書のキーとしては決して使うことができない

タプルはリストに変換できるし、その逆もできる。

  • 組み込みのtuple()関数はリストを引数にとって、同じ要素を持ったタプルを返す。
  • list()関数はタプルを引数にとってリストを返す。

実質的には、

  • tuple()はリストの凍結を行い、
  • list()はタプルの解凍

を行うと考えることができる。

In [100]:
a_tuple_list = list(a_tuple)
a_tuple_list
Out[100]:
['a', 'b', 'mpilgrim', 'z', 'example']
In [101]:
type(a_tuple_list)
Out[101]:
list
In [102]:
a_list_tuple = tuple(a_tuple_list)
a_list_tuple
Out[102]:
('a', 'b', 'mpilgrim', 'z', 'example')
In [103]:
type(a_list_tuple)
Out[103]:
tuple

ブール値のコンテクストでのタプル

タプルはif文のようなブール値のコンテクストで使うことができる。

ブール値のコンテクストでは、空のタプルは偽。

In [104]:
is_it_true(())
no, it's false

要素を1つでももつタプルは真。

In [105]:
is_it_true(('a', 'b'))
yes, it's true
In [106]:
is_it_true((False,))
yes, it's true

1つの要素からなるタプルを作るには、 値の後にカンマ「,」を置かなくてはならない。 もしこのカンマがないと、Pythonは余分な括弧があるだけだと見なしてしまう。 これはエラーにはならないが、タプルは作成されない。

In [107]:
type((False))
Out[107]:
bool
In [108]:
type((False,))
Out[108]:
tuple

複数の値を一度に代入する

Pythonでは、タプルを使って複数の値を一度に代入できる。

  • v は3つの要素を持つタプル
  • (x、y、z)は3つの変数を持つタプル。

一方をもう一方へ代入すると、vの各々の値が、順番通りに各々の変数へ代入される。

In [109]:
v = ('a', 2, True)
(x, y, z) = v
x
Out[109]:
'a'
In [110]:
y
Out[110]:
2
In [111]:
z
Out[111]:
True

これにはあらゆる使い道がある。 仮に、連続する数値に名前を付けたいとしよう。 組み込みの range()関数と多値代入を使うことで、連続する値を素早く代入することができる。

組み込みのrange()関数は、整数のシーケンスを構築する。

MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAYが、ここで定義している変数だ

In [112]:
(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)
MONDAY
Out[112]:
0
In [113]:
TUESDAY
Out[113]:
1
In [114]:
SUNDAY
Out[114]:
6

多値代入は、複数の戻り値を返す関数を作るためにも使える。 これをするには単純に、すべての戻り値を含むタプルを返せばいい。 呼び出し元は、返されたタプルを1つのタプルとして扱うこともできるし、個別の変数に値を代入することもできる。

多くのPythonの標準ライブラリがこれを行っている。

集合

集合は一意な値を詰めた「袋」であり、その要素は順序づけされない。 1つの集合の中には、イミュータブルなデータ型の値なら何でも含めることができる。

2つの集合を作れば、それらを使って和集合・積集合・差集合といった標準的な集合演算を行うこともできる。

集合を作る

1つの値を持った集合を作るには、その値を波括弧 ({}) の中に入れればいい。

In [115]:
a_set = {1}
a_set
Out[115]:
{1}
In [116]:
type(a_set)
Out[116]:
set
In [117]:
a_set = {1, 2}
a_set
Out[117]:
{1, 2}

リストから集合を作ることもできる。

リストから集合を作るには、set()関数を使えばよい

In [118]:
a_list = ['a', 'b', 'mpilgrim', True, False, 42]
a_set = set(a_list)
a_set
Out[118]:
{False, True, 42, 'mpilgrim', 'b', 'a'}
In [119]:
a_list
Out[119]:
['a', 'b', 'mpilgrim', True, False, 42]

空の集合を作ることができる。

空の集合を作るには、set()を引数なしに呼び出せばいい。

空の集合は少し変わった表現 set() で表示される。

In [120]:
a_set = set()
a_set
Out[120]:
set()
In [121]:
type(a_set)
Out[121]:
set

この集合は要素を持たない。

In [122]:
len(a_set)
Out[122]:
0

二つの波括弧で空の集合を作ることはできない。これは実際には空の集合ではなく、「空の辞書」を作ってしまう。

In [123]:
not_sure = {}
type(not_sure)
Out[123]:
dict

集合を変更する

集合に値を追加するには2つの異なる方法がある。

  • add()メソッド
  • update()メソッド

add()メソッドは単一の引数(どんな型でもよい)をとり、与えられた値を集合に追加する。

In [124]:
a_set = {1, 2}
a_set.add(4)
a_set
Out[124]:
{1, 2, 4}
In [125]:
len(a_set)
Out[125]:
3

集合なので、既にその集合に含まれている値を追加しようとしても、何も起こらない。

In [126]:
a_set.add(1)
a_set
Out[126]:
{1, 2, 4}
In [127]:
len(a_set)
Out[127]:
3
In [128]:
a_set = {1, 2, 3}
a_set
Out[128]:
{1, 2, 3}

update()メソッドは1つの引数として集合をとり、その集合のすべての要素を元の集合に追加する。

In [129]:
a_set.update({2, 4, 6})
a_set
Out[129]:
{1, 2, 3, 4, 6}

実際にはupdate()メソッドは任意の数の引数と共に呼び出すことができる。 2つの集合と共に呼び出すと、update()メソッドは各々の集合の各要素を元の集合に追加する。

In [130]:
a_set.update({3, 6, 9}, {1, 2, 3, 5, 8, 13})
a_set
Out[130]:
{1, 2, 3, 4, 5, 6, 8, 9, 13}

update()メソッドは、リストのようにいくつもの異なるデータ型から構成されるオブジェクトを受け取ることができる。リストと共に呼び出されると、update()メソッドはリストのすべての要素を元の集合に追加する。

In [131]:
a_set.update([10, 20, 30])
a_set
Out[131]:
{1, 2, 3, 4, 5, 6, 8, 9, 10, 13, 20, 30}

集合から値を取り除く

集合から値を取り除くには3つの異なる方法がある。 始めの2つは

  • discard()メソッド
  • remove()メソッド
In [132]:
a_set = {1, 3, 6, 10, 15, 21, 28, 36, 45}
a_set
Out[132]:
{1, 3, 6, 10, 15, 21, 28, 36, 45}

discard()メソッドは引数として1つの値を受け取り、その値を集合から取り除く。

In [133]:
a_set.discard(10)
a_set
Out[133]:
{1, 3, 6, 15, 21, 28, 36, 45}

集合に存在しない値を引数としてdiscard()メソッドを呼び出したときは、何も行われない。エラーも起きない。(差集合)

In [134]:
a_set.discard(10)
a_set
Out[134]:
{1, 3, 6, 15, 21, 28, 36, 45}

remove()メソッドも同じく引数として1つの値を受け取り、その値を集合から取り除く。

In [135]:
a_set.remove(21)
a_set
Out[135]:
{1, 3, 6, 15, 28, 36, 45}

値が集合の中に存在しない場合は、remove()メソッドはKeyError例外を送出する。

In [136]:
a_set.remove(21)
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-136-e1324b18ad7c> in <module>()
----> 1 a_set.remove(21)

KeyError: 21

リストのように、集合はpop()メソッドを持っている。

pop()メソッドは1つの値を取り除いてその値を返す。 ただし、集合の要素は順序付けされていないので、集合には「最後の」値というものはなく、したがって、どの値が取り除かれるのかを制御する方法はない。 どれが取り除かれるかは完全に不定だ。

In [137]:
a_set = {1, 3, 6, 10, 15, 21, 28, 36, 45}
a_set.pop()
Out[137]:
1
In [138]:
a_set.pop()
Out[138]:
3
In [139]:
a_set.pop()
Out[139]:
36
In [140]:
a_set
Out[140]:
{6, 10, 15, 21, 28, 45}

clear()メソッドはすべての値を集合から削除し、空集合になる。

In [141]:
a_set.clear()
a_set
Out[141]:
set()

空の集合からpopしようとすると、KeyError例外が出される。

In [142]:
a_set.pop()
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-142-e21c77f61fc4> in <module>()
----> 1 a_set.pop()

KeyError: 'pop from an empty set'

一般的な集合演算

Pythonのsetはいくつかの一般的な集合演算をサポートしている。

ある値が集合に含まれているかを確認するには、in演算子を使えばよい。これはリストの場合と同様に機能する。

In [143]:
a_set = {2, 4, 5, 9, 12, 21, 30, 51, 76, 127, 195}
30 in a_set
Out[143]:
True
In [144]:
31 in a_set
Out[144]:
False

union()メソッドは、どちらかの集合に含まれるすべての要素を含んだ新しい集合を返す。(和集合)

In [145]:
b_set = {1, 2, 3, 5, 6, 8, 9, 12, 15, 17, 18, 21}
a_set.union(b_set)
Out[145]:
{1, 2, 3, 4, 5, 6, 8, 9, 12, 15, 17, 18, 21, 30, 51, 76, 127, 195}

intersection()メソッドは、両方の集合に含まれるすべての要素を含んだ新しい集合を返す。(積集合)

In [146]:
a_set.intersection(b_set)
Out[146]:
{2, 5, 9, 12, 21}

difference()メソッドは、a_setには含まれるがb_setには含まれていないすべての要素を含んだ新しい集合を返す。(差集合)

In [147]:
a_set.difference(b_set) 
Out[147]:
{4, 30, 51, 76, 127, 195}

symmetric_difference()メソッドは、どちらか一方だけの集合に含まれるすべての要素を含んだ新しい集合を返す。(対称差)

In [148]:
a_set.symmetric_difference(b_set)
Out[148]:
{1, 3, 4, 6, 8, 15, 17, 18, 30, 51, 76, 127, 195}

以上のメソッドのうち、3つは対称的。

b_setからa_setの対称差 (symmetric difference) は、a_setからb_setの対称差。

In [149]:
b_set.symmetric_difference(a_set)
Out[149]:
{1, 3, 4, 6, 8, 15, 17, 18, 30, 51, 76, 127, 195}
In [150]:
b_set.symmetric_difference(a_set) == a_set.symmetric_difference(b_set)
Out[150]:
True

2つの集合の和集合 (union) も対称的。

In [151]:
b_set.union(a_set) == a_set.union(b_set)
Out[151]:
True

2つの集合の積集合 (intersection) も対称的。

In [152]:
b_set.intersection(a_set) == a_set.intersection(b_set)
Out[152]:
True

2つの集合の差 (difference) は対称的ではない。

In [153]:
b_set.difference(a_set) == a_set.difference(b_set)
Out[153]:
False

集合に尋ねることのできる事柄。

a_setはb_setの部分集合 (subset) 。 つまり a_set のすべての要素は set にも含まれている。

In [154]:
a_set = {1, 2, 3}
b_set = {1, 2, 3, 4}
a_set.issubset(b_set) 
Out[154]:
True

逆の言い方をすると、a_setのすべての要素はb_setの要素でもあるので、 「b_setはa_setの上位集合 (superset)」。

In [155]:
b_set.issuperset(a_set)
Out[155]:
True

b_set に含まれていない値をa_setに追加すると、たちまち両方の結果はFalseになる。

In [156]:
a_set.add(5)
a_set.issubset(b_set)
Out[156]:
False
In [157]:
b_set.issuperset(a_set)
Out[157]:
False

ブール値のコンテクストでの集合

ブール値のコンテクストでは、空の集合は偽。

In [158]:
is_it_true(set())
no, it's false

要素を1つでも持つ集合は真。

In [159]:
is_it_true({'a'})
yes, it's true
In [160]:
is_it_true({False})
yes, it's true

辞書

辞書は、「キーと値のペアからなる順序付けされていない集合」。

辞書にキーを追加するときは、そのキーに対応する値も同時に追加しなければならない(この値はあとでいつでも変更できる)。

Pythonの辞書は、既知のキーをもとに値を取得するのは効率的に行えるようになっているが、その逆方向はそうなっていない。

辞書を作る

構文は集合に似ているが、値の代わりにキーと値のペアが必要になる。辞書を作ったあとは、キーを指定することで値を取り出せる。

各々の要素はキーと値のペアであり、要素の集合の全体は波括弧「{}」に包まれている。

In [161]:
a_dict = {'server': 'db.diveintopython3.org', 'database': 'mysql'}
a_dict
Out[161]:
{'database': 'mysql', 'server': 'db.diveintopython3.org'}

'server'はキーだ。このキーに関連づけられた値はa_dict['server']によって参照でき、その値は'db.diveintopython3.org'。

In [162]:
a_dict['server']
Out[162]:
'db.diveintopython3.org'
In [163]:
a_dict['database']
Out[163]:
'mysql'

値はキーによって取得できるが、キーを値によって取得することはできない。 つまり、a_dict['server']は'db.diveintopython3.org'になるが、'db.diveintopython3.org'はキーではないので、a_dict['db.diveintopython3.org']は例外を出す。

In [164]:
a_dict['db.diveintopython3.org']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-164-15da1c9e76d5> in <module>()
----> 1 a_dict['db.diveintopython3.org']

KeyError: 'db.diveintopython3.org'

辞書を変更する

辞書には、事前に定められた上限サイズというものは一切ない。新しいキーと値のペアをいつでも辞書に追加できるし、既在のキーに対応する値もいつでも変更可能。

In [165]:
a_dict
Out[165]:
{'database': 'mysql', 'server': 'db.diveintopython3.org'}

辞書に重複したキーを持たせることはできない。既存のキーに値を代入すると、古い値が追い出される。

In [166]:
a_dict['database'] = 'blog'
a_dict
Out[166]:
{'database': 'blog', 'server': 'db.diveintopython3.org'}

キーと値のペアはいつでも追加できる。 この構文は既存の値を変更するときとまったく同じ。

In [167]:
a_dict['user'] = 'mark'
a_dict      
Out[167]:
{'database': 'blog', 'server': 'db.diveintopython3.org', 'user': 'mark'}
In [168]:
a_dict['user'] = 'dora'
a_dict
Out[168]:
{'database': 'blog', 'server': 'db.diveintopython3.org', 'user': 'dora'}

辞書のキーについては大文字と小文字が区別されるので、この文は新たなキーと値のペアを作り、既存の値は書き換えない。

In [169]:
a_dict['User'] = 'mark'
a_dict
Out[169]:
{'User': 'mark',
 'database': 'blog',
 'server': 'db.diveintopython3.org',
 'user': 'dora'}

値が混在する辞書

辞書は文字列しか扱えないわけではない。 辞書の値はどんなデータ型でもよく、 例えば、整数型、ブール値、任意のオブジェクトを使うことができるし、他の辞書を値にすることもできる。

また1つの辞書において、全ての値が同じ型である必要はなく、必要に応じて様々な型を混ぜて使うことができる。 辞書のキーとして使える型は値よりも制限されているが、文字列や数値、その他いくつかの型を使うことができる。 辞書のキーも、一つの辞書の中で様々なデータ型を混ぜて使うことができる。

リストや集合と同じように、len()関数は辞書に含まれるキーの数を教えてくれる。

In [170]:
SUFFIXES = { 
    1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
    1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
}

len(SUFFIXES)
Out[170]:
2

リストや集合と同様に、特定のキーが辞書に含まれているかどうかを調べるには、in演算子が使える。

In [171]:
1000 in SUFFIXES
Out[171]:
True

1000は辞書SUFFIXESのキーであり、 対応する値は8つの要素(正確には8つの文字列)を持つリスト。

In [172]:
SUFFIXES[1000]
Out[172]:
['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
In [173]:
SUFFIXES[1024]
Out[173]:
['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']

SUFFIXES[1000]はリストなので、このリストの個々の要素を0から始まるインデックスで指定できる。

In [174]:
SUFFIXES[1000][3]
Out[174]:
'TB'

ブール値のコンテクストでの辞書

辞書はif文のようなブール値のコンテクストで使うことができる。

ブール値のコンテクストでは、空の辞書は偽.

In [175]:
is_it_true({})
no, it's false

キーと値のペアを1つでも持っている辞書は真。

In [176]:
is_it_true({'a': 1})
yes, it's true

None

NoneはPythonの特別な定数で、これはnull値(無効値)である。 NoneはFalseではないし、0でもないし、空の文字列でもない。NoneをNone以外の値と比較すると、常にFalseが返る。

Noneは唯一のnull値であり、Noneは自身のデータ型 (NoneType) を持っている。Noneは任意の変数へ代入できるが、他のNoneTypeのオブジェクトを作ることはできない。Noneを値として持つすべての変数は互いに等しい。

In [177]:
type(None)
Out[177]:
NoneType
In [178]:
None == False
Out[178]:
False
In [179]:
None == 0
Out[179]:
False
In [180]:
None == ''
Out[180]:
False
In [181]:
None == None
Out[181]:
True
In [182]:
x = None
x == None
Out[182]:
True
In [183]:
y = None
x == y
Out[183]:
True

ブール値のコンテクストでのNone

ブール値のコンテクストでは、

  • Noneは偽
  • not Noneは真。
In [184]:
is_it_true(None)
no, it's false
In [185]:
is_it_true(not None)
yes, it's true

参考リンク