●Hello, Python3!
- Linux 系 OS の場合、Python はインストール済みのことが多い
- Xubuntu (17.10) の場合、次のバージョンがインストールされている
mhiroi@mhiroi-VirtualBox:~$ python -V
Python 2.7.14
mhiroi@mhiroi-VirtualBox:~$ python3 -V
Python 3.6.3
デフォルトでは Python3 のコマンド名が python3、python と入力すると Python2 が起動するので注意すること
Windows の場合、Python Releases for Windows (https://www.python.org/downloads/windows/) から適当なインストーラをダウンロードして実行するだけ
C>python -V
Python 3.6.4
Python3 の場合、print は関数になったのでカッコが必要になる
>>> print "hello, world"
File "", line 1
print "hello, world"
^
SyntaxError: Missing parentheses in call to 'print'. Did you mean
print("hello, world")?
>>> print("hello, world")
hello, world
コメントは # から改行まで
ソースコードのエンコーディングはデフォルトで UTF-8
●基本的なデータ型
- 真偽値 (bool)
- None (値が無いことを表すデータ (型 NoneType の唯一の値))
- 数
- Python3 の数はプリミティブな型として、整数 (int)、浮動小数点数 (float)、複素数 (complex) の 3 種類がある
- 整数は Python2 の長整数型 (多倍長整数) と同じ
- Python2 と違って末尾に L (または l) を付ける必要はない
- 接頭辞 0b は 2 進数、0o は 8 進数 (Python2 は 0 または 0o)、0x は 16 進数を表す
- 浮動小数点数はC言語の倍精度浮動小数点数 (double) と同じで、範囲は約 1e-307 から 1e+308 まで
- 複素数は 実数部 + 虚数部 で表す (虚数単位は J または j を使って表す)
- 複素数 a + bj の a, b は浮動小数点数
>>> 123456789
123456789
>>> 0xffff
65535
>>> 0o7777
4095
>>> 0b1111
15
>>> 1.2345
1.2345
>>> 1.2345e123
1.2345e+123
>>> 1.1+2.2j
(1.1+2.2j)
>>> 1.1-2.2j
(1.1-2.2j)
文字列
- 文字列 (string) はシングルクオート ' で囲むか、ダブルクオート " で囲んで表す
- Python3 の文字列はユニコード
- Python2 と違って先頭に u を付ける必要はない (付けても動作する)
- 文字列の中では \n (改行) や \t (タブ) といったエスケープシーケンスを含めることができる
- エスケープシーケンスを無効にしたい場合は、文字列の前に r または R を付ける (これを raw string という)
- Perl や Unix 系のシェルのような「変数展開」をしたい場合は、文字列の前に f または F を付ける
- これを「フォーマット済み文字列リテラル (f-string)」という
f'... {式} ...' => '... 値 ...'
f-string は文字列の中に {式} を見つけると、{式} の部分を式を評価した値に置き換えた文字列を生成する
f と r を組み合わせた接頭辞 fr, rf も使用することができる
三重引用符 ''' (または """) で囲むと、入力データがそのまま文字列になる
Perl や UNIX 系のシェルにあるヒアドキュメント (here-document) という機能とよく似ている
>>> 'hello, world'
'hello, world'
>>> "hello, world"
'hello, world'
>>> "こんにちは"
'こんにちは'
>>> "abc\ndef"
'abc\ndef'
>>> print("abc\ndef")
abc
def
>>> "abc\tdef"
'abc\tdef'
>>> print("abc\tdef")
abc def
>>> r"abc\ndef"
'abc\\ndef'
>>> print(r"abc\ndef")
abc\ndef
>>> """
... abc
... def
... ghi
... """
'\nabc\ndef\nghi\n'
リスト
- Python のリスト (list) は他言語の一次元配列と同様のデータ構造
- 関数型言語でよく使われるリスト (連結リスト) ではない
- リストの大きさは Python が自動的に調整する (可変長配列)
- リストに格納されたデータを「要素」という
- 要素のデータ型は異なっていてもかまわない
- リストの要素は整数値で指定する (これを「添字 (subscripts)」という)
- Python の場合、添字はC言語と同じく 0 から始まる
- リストは角カッコ [ ] で囲み、要素をカンマ ( , ) で区切って表す
- [ ] は要素が一つもない空リストになる
- リストの要素は角カッコ [ ] を使ってアクセスする (C言語や Perl と同じ)
- 添字に負の整数を指定するとリストの後ろから数える
>>> a = [1, 2, 3, 4, 5]
>>> a
[1, 2, 3, 4, 5]
>>> print(a)
[1, 2, 3, 4, 5]
>>> a[0]
1
>>> a[4]
5
>>> a[3] = 10
>>> a
[1, 2, 3, 10, 5]
>>> a[-1]
5
>>> a[-2]
10
- Python はあらかじめ変数やそのデータ型を宣言する必要はない
- 変数に値をセットすることを「代入」という
- 代入には演算子 = を使う (C言語や Perl と同じ)
- 対話モードで変数名を入力するとその値が表示される (print() で表示することもできる)
- リストの主な操作メソッド (xs はリスト)
- xs.append(x), 最後尾に要素を追加する
- xs.pop(), 最後尾から要素を削除する
- xs.index(x), 要素 x を探して位置を返す
- xs.insert(i,x), i 番目に要素 x を挿入する
- del xs[i], i 番目の要素を削除する
- del xs[s:e] のように「スライス」を使用することができる
- スライスはあとで説明する
- xs.remove(x), 要素 x を削除する
>>> xs = [1, 2, 3, 4, 5]
>>> xs.append(6)
>>> xs
[1, 2, 3, 4, 5, 6]
>>> xs.pop()
6
>>> xs
[1, 2, 3, 4, 5]
>>> xs.index(5)
4
>>> xs.index(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: 0 is not in list
>>> xs.insert(0, 10)
>>> xs
[10, 1, 2, 3, 4, 5]
>>> del xs[0]
>>> xs
[1, 2, 3, 4, 5]
>>> xs.remove(3)
>>> xs
[1, 2, 4, 5]
- リストは入れ子にすることができる (これで多次元配列を表すことができる)
- N 次元配列の場合、要素のアクセスは角カッコを N 個使う
>>> a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> a
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> a[0]
[1, 2, 3]
>>> a[1]
[4, 5, 6]
>>> a[2]
[7, 8, 9]
>>> a[0][0]
1
>>> a[2][2]
9
- a の 0 番目の要素はリスト [1, 2, 3] で、そのリストの 0 番目の要素は 1
- この要素は角カッコを 2 つ使って a[0][0] とアクセスすることができる
- a[2][2] は 2 番目の要素 [7, 8, 9] の 2 番目の要素にアクセスするので 9 になる
タプル
- タプル (tuple) は複数の要素を格納した immutable なデータ構造 (値を更新できないリスト)
- タプルは丸カッコ ( ) で囲み、要素をカンマ ( , ) で区切る
- ( ) は空タプルを表す
- タプルを生成するとき丸カッコを省略できる場合がある (変数に代入するときなど)
- タプルはリストと同じく角カッコ [ ] で要素にアクセスする
- 要素が一つのタプルは、要素の後ろにカンマを付ける必要がある
>>> b = (1, 2, 3, 4, 5)
>>> b
(1, 2, 3, 4, 5)
>>> b = 1, 2, 3, 4, 5
>>> b
(1, 2, 3, 4, 5)
>>> b[0]
1
>>> b[4]
5
>>> b[-1]
5
>>> b[-2]
4
>>> c = (1,)
>>> c
(1,)
>>> c[0]
1
シーケンス
- 一般に、データを一列に並べたデータ構造を「シーケンス (sequence)」という
- 文字列、リスト、タプルはシーケンス
- Python にはシーケンスに適用できる共通な操作や関数 (メソッド) が用意されている
- シーケンスの要素は角カッコでアクセスできる
- Python には文字を表すデータ型がないので、文字列の要素は文字列になる
- 演算子 + は同じ型のシーケンスを連結した新しいシーケンスを返す
- 演算子 * はシーケンスを n 回繰り返した新しいシーケンスを返す
- 関数 len() はシーケンスの長さ (要素数) を返す
- x in seq はシーケンスの要素に x があれば真を返す
- x not in seq はシーケンスの要素に x がなければ真を返す
- スライスでシーケンスの部分列をコピーすることができる
- seq[start:end], start から end - 1 まで
- seq[start:], start から最後尾まで
- seq[:end], 先頭から end - 1 まで
- seq[:], 先頭から最後尾まで
- 次のように step を指定すると step 刻みで部分列をコピーする
- start:end:step, start::step, :end:step, ::step
- リストの場合、スライスへの代入が可能
- 関数 list() は引数をリストに変換する
- 関数 tuple() は引数をタプルに変換する
- 変換できない場合はエラー
>>> a = "hello"
>>> len(a)
5
>>> 'h' in a
True
>>> 'a' in a
False
>>> 'a' not in a
True
>>> 2 in [1, 2, 3, 4, 5]
True
>>> a + ", world"
'hello, world'
>>> a
'hello'
>>> [1, 2, 3] + [4, 5, 6]
[1, 2, 3, 4, 5, 6]
>>> a * 5
'hellohellohellohellohello'
>>> a
'hello'
>>> [0] * 10
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
>>> for i in range(len(a) + 1):
... print(a[:i])
...
h
he
hel
hell
hello
>>> list(a)
['h', 'e', 'l', 'l', 'o']
>>> tuple(a)
('h', 'e', 'l', 'l', 'o')
- シーケンスを分解して要素を複数の変数に代入することができる (シーケンスのアンパック)
- 変数の個数と要素の個数が合わないとエラー
- 変数の値を交換することも簡単にできる
>>> x, y, z = 1, 2, 3
>>> x
1
>>> y
2
>>> z
3
>>> x, y, z = [10, 20, 30]
>>> x
10
>>> y
20
>>> z
30
>>> x, y, z = "xyz"
>>> x
'x'
>>> y
'y'
>>> z
'z'
>>> x, y = y, x
>>> x
'y'
>>> y
'x'
ディクショナリ
- ディクショナリ (dictionary) は「連想配列」のことで、他の言語ではハッシュと呼ばれることもある
- ディクショナリは「キー (key)」というデータを使って要素を指定する
- 一般に、連想配列のキーには文字列が用いられる
- Python では変更不能なデータ型(数値、文字列、タプル)であれば、キーとして使用することができる
- ディクショナリは中カッコ { } で囲み、要素をカンマで区切って表す
- 要素は「キー: データ」で指定する
- { } は空のディクショナリを表す
- ディクショナリのアクセスはリストと同様に角カッコを使う
- キーの有無は演算子 in, not in で調べることができる
- 要素数は len() で求めることができる
>>> dic = {'foo': 1, 'bar': 2, 'baz': 3, 'oops': 4}
>>> dic
{'foo': 1, 'bar': 2, 'baz': 3, 'oops': 4}
>>> len(dic)
4
>>> dic['foo']
1
>>> dic['oops'] = 100
>>> dic
{'foo': 1, 'bar': 2, 'baz': 3, 'oops': 100}
>>> dic['oops!'] = 200
>>> dic
{'foo': 1, 'bar': 2, 'baz': 3, 'oops': 100, 'oops!': 200}
>>> 'foo' in dic
True
>>> 'fo' in dic
False
>>> 'fo' not in dic
True
- 一般に、複数の要素を格納するデータ型を「コレクション (collection)」とか「コンテナ (container)」と呼ぶ
- リスト、タプル、文字列、ディクショナリはコレクション
- イテレータ (iterator) はコレクションから要素を一つずつ順番に取り出す機能
- イテレータを実装しているオブジェクトを iterable という
- イテレータはあとで詳しく説明する
- ディクショナリの主な操作メソッド
- dict(), ディクショナリを生成する
- del dic[key], キーと値を削除する
- dic.clear(), ディクショナリを空にする
- dic.keys(), ディクショナリ内のキーを取り出す iterable を返す
- dic.values(), ディクショナリ内の値を取り出す iterable を返す
- dic.items(), ディクショナリ内のキーと値を取り出す iterable を返す
- dic1.update(dic2), ディクショナリ dic1 に dic2 の要素を追加する
- Python2 の keys(), values(), items() はリストを返していたが、Python3 では iterable を返す
- リストが必要な場合は list() を使う
>>> dic1 = dict()
>>> dic1
{}
>>> dic2 = dict([('a', 1), ('b', 2), ('c', 3)])
>>> dic2
{'a': 1, 'b': 2, 'c': 3}
>>> dic2.keys()
dict_keys(['a', 'b', 'c'])
>>> dic2.values()
dict_values([1, 2, 3])
>>> dic2.items()
dict_items([('a', 1), ('b', 2), ('c', 3)])
>>> list(dic2.keys())
['a', 'b', 'c']
>>> list(dic2.values())
[1, 2, 3]
>>> list(dic2.items())
[('a', 1), ('b', 2), ('c', 3)]
>>> dic1.update(dic2)
>>> dic1
{'a': 1, 'b': 2, 'c': 3}
>>> dic2
{'a': 1, 'b': 2, 'c': 3}
>>> del dic1['a']
>>> dic1
{'b': 2, 'c': 3}
>>> dic2.clear()
>>> dic2
{}
セット
- セット (set) は集合を表すのに便利なデータ構造 (コレクション)
- セットはリストと違って要素に順番はなく、重複した要素を含まないところが特徴
- セットは中カッコ { } で囲み、要素をカンマで区切って表す
- ただし、{ } で空のセットを生成すことはできない (関数 set() を使う)
- 要素の有無は演算子 in, not in で調べることができる
- セットの主な操作メソッド
- set(), セットを生成する
- len(s), セットの要素数を返す
- s.add(x), 要素 x を追加する
- s.remove(x), 要素 x を削除する
- s.clear(), セットを空にする
- s.update(xs), コレクション xs の要素を追加する
>>> s1 = {1, 2, 3, 4, 5}
>>> s1
{1, 2, 3, 4, 5}
>>> set([1, 2, 3, 4, 5])
{1, 2, 3, 4, 5}
>>> set((1, 2, 3, 4, 5))
{1, 2, 3, 4, 5}
>>> 1 in s1
True
>>> 10 in s1
False
>>> len(s1)
5
>>> s1.add(10)
>>> s1
{1, 2, 3, 4, 5, 10}
>>> 10 in s1
True
>>> s1.remove(1)
>>> s1
{2, 3, 4, 5, 10}
>>> s2 = set()
>>> s2
set()
>>> s2.update(s1)
>>> s2
{2, 3, 4, 5, 10}
- セットには集合演算を行うメソッドが用意されている
- s1.issubset(s2), s1 が s2 の部分集合であれば真を返す
- s1.issuperset(s2), s2 が s1 の部分集合であれば真を返す
- s1.intersection(s2), s1 と s2 の積集合を求める (s1 & s2)
- s1.union(s2), s1 と s2 の和集合を求める (s1 | s2)
- s1.difference(s2), s1 と s2 の差集合を求める (s1 - s2)
- s1.symmetric_difference(s2), s1 と s2 の両方にちょうど 1 つだけ現れる要素を求める (s1 ^ s2)
>>> s1 = {1,2,3,4}
>>> s2 = {3,4,5,6}
>>> s1.union(s2)
{1, 2, 3, 4, 5, 6}
>>> s1.intersection(s2)
{3, 4}
>>> s1.difference(s2)
{1, 2}
>>> s1.symmetric_difference(s2)
{1, 2, 5, 6}
>>> s1.issubset(s2)
False
>>> s1.issubset({1,2,3,4,5,6})
True
●基本的な演算子
- 算術演算子 (+, -, *, /, //, %, **)
- x / y の結果は浮動小数点数になる
- x // y は整数の徐算で、結果も整数になる
- x ** y は x の y 乗を計算する
- 比較演算子 (==, !=, <, >, <=, >=, is, is not)
- x is y は x と y が同一オブジェクトのときに真を返す
- x is not y は x と y が異なるオブジェクトのときに真を返す
- 論理演算子 (not, and, or)
- ビット演算子 (~, &, |, ^, <<, >>)
- 代入演算子 (=, +=, -=, *=, /=, //=, %=, **=, &=, |=, ^=, <<=, >>=)
- 演算子 in は要素がコレクションに含まれていれば真を返す
- 演算子 not in は要素がコレクションに含まれていなければ真を返す
- インクリメント (++), デクリメント (--) は存在しない
●基本的な制御構造
- 一般に、複数の処理をまとめたものをブロック (block) という
- ブロックは定義された処理を順番に実行する
- インデントでブロックを表すのが Python の大きな特徴のひとつ
- if 文
if test_a:
処理A1
処理A2
...
elif test_b:
処理B1
処理B2
...
else:
処理C1
処理C2
...
偽と判定されるのは False, 0, 0.0, "", None
[], (), {}, set() など空のコレクションも偽と判定される
それ以外の値は真と判定される
>>> if True:
... print("OK")
... else:
... print("NG")
...
OK
>>> if False:
... print("OK")
... else:
... print("NG")
...
NG
Python には条件式もある
then式 if 条件式 else else式
条件式を評価して結果が真の場合、then式の評価結果を返す
偽の場合は else式 の評価結果を返す
他の言語の三項演算子と同様に使用することができる
>>> a = 1 if True else 2
>>> a
1
>>> a = 1 if False else 2
>>> a
2
while 文
while test:
処理A
処理B
処理C
else:
処理Z
Python の while は else 節を定義することができる
else 節は while ループが終了したあとに実行される
>>> a = 5
>>> while a > 0:
... print("hello, world")
... a -= 1
...
hello, world
hello, world
hello, world
hello, world
hello, world
for 文
for 変数, ... in コレクション:
処理A
処理B
処理C
else:
処理Z
for はコレクションから要素を一つずつ取り出して変数に代入し、ブロックに書かれている処理を繰り返し実行する
C言語の for ではなく、Perl の foreacch と同じ
インデックス (添字) が必要なときは関数 enumerate() が便利
実際には、コレクションでなくても iterable であれば for を利用できる
while と同様に for も else 節を定義できる
>>> for a in [1, 2, 3, 4, 5]:
... print(a)
...
1
2
3
4
5
>>> for i, x in enumerate([1, 2, 3, 4, 5]):
... print(i, x)
...
0 1
1 2
2 3
3 4
4 5
>>> dic = {'foo': 10, 'bar': 20, 'baz': 30}
>>> for k, v in dic.items():
... print(k, v)
...
foo 10
bar 20
baz 30
while や for の繰り返しは break によって脱出することができる
このとき else 節は実行されない
continue は繰り返しの先頭に戻る
>>> for a in [1,2,3,4,5]:
... if a == 4: break
... if a == 2: continue
... print(a)
...
1
3
関数 range() は数列を生成する iterable を返す
for と一緒に使うと便利
range(end)
range(start, end)
range(start, end, step)
range() は start から end 未満の数列を step 刻みで生成する
start が省略されると 0 から始まる
step を省略すると増分値は 1 になる
Python3 の range() は Python2 の xrange() と同じ (xrange() は Python3 で廃止)
リストを生成したい場合は list() や「内包表記」を使う
>>> for x in range(5):
... print("hello, world")
...
hello, world
hello, world
hello, world
hello, world
hello, world
>>> list(range(1, 10))
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(range(1, 10, 2))
[1, 3, 5, 7, 9]
内包表記
- Python3 は [ ] や { } の中に for 文を書いてリスト、ディクショナリ、セットを生成することができる
- これを「内包表記」とか「内包表現」という
[式 for 変数, ... in コレクション] => リスト
{式 for 変数, ... in コレクション} => セット
{式(key): 式(value) for 変数, ... in コレクション} => ディクショナリ
- for 文の後ろに if 文を続けると、条件部が真のときに式を評価してコレクションを生成する
- for 文の後ろに for 文を続けると二重ループになる
- 内包表記は強力な機能でとても便利
>>> [x for x in range(1, 10)]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [x * x for x in range(1, 10)]
[1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> [x for x in range(1, 10) if x % 2 == 0]
[2, 4, 6, 8]
>>> [(x, y) for x in range(1, 4) for y in range(4, 7)]
[(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)]
>>> [(x, y) for x in range(1, 6) for y in range(x, 6) if x != y]
[(1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5)]
>>> dic = {'foo': 10, 'bar': 20, 'baz': 30}
>>> {k: v * 2 for k, v in dic.items()}
{'foo': 20, 'bar': 40, 'baz': 60}
>>> {k: v for k, v in dic.items() if k[0] == 'b'}
{'bar': 20, 'baz': 30}
●関数
def 名前(仮引数名, ...):
処理A
...
処理Z
Python の関数はオブジェクト (関数オブジェクト) で、名前で指定した変数に格納される
関数の返り値は return を使って返す (C言語と同じ)
return のない関数は None を返す
関数呼び出しは 関数名(実引数, ...)
関数の引数は局所変数、関数の中で値を代入した変数も局所変数として扱われる
C言語や Perl のように、局所変数の宣言を行う必要はない
関数の中で大域変数の値を書き換えたい場合は global 宣言すること
>>> def square(x):
... return x * x
...
>>> square
<function square at 0x000002032BA20158>
>>> square(2)
4
仮引数名 = 値 の形式で引数のデフォルト値を指定できる (デフォルト引数)
デフォルト引数は省略可 (デフォルト値が使用される)
実引数は name = value の形式で渡すことができる (キーワード引数)
name と同じ名前の仮引数に value が渡される
仮引数に * を付けると可変個引数になる
残った実引数はタプルに格納して渡される
仮引数に ** を付けると、残ったキーワード引数をディクショナリに格納して仮引数に渡す
*args や **args は最後に定義すること
>>> def foo(a, b = 2, *c, **d):
... print(a, b, c, d)
...
>>> foo(1)
1 2 () {}
>>> foo(10, 20)
10 20 () {}
>>> foo(10, 20, 30, 40)
10 20 (30, 40) {}
>>> foo(a = 100, b = 200, x = 300, y = 400)
100 200 () {'x': 300, 'y': 400}
>>> foo(1, 2, 3, 4, 5, x = 300, y = 400)
1 2 (3, 4, 5) {'x': 300, 'y': 400}
タプル tup やリスト xs をアンパックして関数に渡すときは、*tup, *xs のように * を付ける
ディクショナリ dic をアンパックして関数に渡すときは、**dic のように ** を付ける
このとき、ディクショナリのキーが仮引数名、値が実引数になる (キーワード引数に展開される)
>>> def bar(a, b, c):
... print(a, b, c)
...
>>> bar(*[1,2,3])
1 2 3
>>> bar(*(1,2,3))
1 2 3
>>> bar(**{'a': 10, 'b': 20, 'c': 30})
10 20 30
再帰定義
- fact() は階乗を、fibo() はフィボナッチ数を計算する関数
- fibo() は二重再帰なので実行時間はとても遅い
- 末尾再帰最適化は行われない
>>> def fact(n):
... if n == 0: return 1
... return n * fact(n - 1)
...
>>> fact(10)
3628800
>>> fact(50)
30414093201713378043612608166064768844377641568960512000000000000
>>> def fibo(n):
... if n == 0 or n == 1: return n
... return fibo(n - 1) + fibo(n - 2)
...
>>> for x in range(10):
... print(fibo(x))
...
0
1
1
2
3
5
8
13
21
34
高階関数とラムダ式
- Python は関数型言語のように関数を変数に代入したり、引数として渡すことができる
- また、値として関数を返すこともできるので、関数を作る関数を定義することもできる
- 関数を引数として受け取る関数を「高階関数 (higher order function)」という
- Python3 の map() と filter() は iterable を受け取ってイテレータを返す
- reduce() はモジュール functools に移動した
- reduce() も iterable を受け取ることができる
- ラムダ式は無名の関数を生成する
lambda 引数, ...: 式
ラムダ式は式の評価結果を返す (return は不要)
>>> lambda x: x * x
<function <lambda> at 0x00000211BCB90158>
>>> (lambda x: x * x)(10)
100
>>> square = lambda x: x * x
>>> square(10)
100
>>> list(map(lambda x: x * x, range(1, 11)))
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>> list(filter(lambda x: x % 2 == 0, range(1, 11)))
[2, 4, 6, 8, 10]
>>> import functools
>>> functools.reduce(lambda x, y: x + y, range(1, 11), 0)
55
クロージャと局所関数
>>> def adder(n):
... return lambda x: x + n
...
>>> add10 = adder(10)
>>> add10(1)
11
>>> add10(10)
20
>>> add100 = adder(100)
>>> add100(1000)
1100
>>> def make_fibo():
... a, b = 0, 1
... def fibo():
... nonlocal a, b
... c = a
... a, b = b, a + b
... return c
... return fibo
...
>>> g = make_fibo()
>>> for _ in range(10):
... print(g())
...
0
1
1
2
3
5
8
13
21
34
- Python は局所関数を定義できる
- 定義した局所関数やラムダ式を返す関数も作成できる
- 関数 make_fibo() はフィボナッチ数列を生成する関数を返す
- Python3 の場合、nonlocal 宣言すると外側の局所変数の値を書き換えることができる
- なお、数列を生成するようなプログラムは「ジェネレータ」という機能を使ったほうが簡単
部分適用
- モジュール functools の関数 partial() を使うと、関数の「部分適用」を行うことができる
- 関数の部分適用とは、指定した引数に値を設定して、残りの引数を受け取る関数を生成すること
- 詳細はライブラリリファレンスマニュアル 10.2. functools を参照
>>> import functools
>>> f = functools.partial(map, lambda x: x * x)
>>> list(f([1, 2, 3, 4]))
[1, 4, 9, 16]
●イテレータとジェネレータ
- 関数 iter() は引数のオブジェクトをイテレータに変換する
- 実際にはオブジェクトのメソッド __iter__() を呼び出す
- 変換できない (__iter__() が実装されていない) 場合はエラー (TypeError)
- 関数 next() はイテレータからデータを一つ取り出す
- 実際にはオブジェクトのメソッド __next__() を呼び出す
- 空の場合は例外 StopIteration を送出する
>>> a = iter([1, 2, 3, 4, 5])
>>> a
<list_iterator object at 0x000002604764D9E8>
>>> next(a)
1
>>> next(a)
2
>>> next(a)
3
>>> next(a)
4
>>> next(a)
5
>>> next(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
ジェネレータ (generator) はイテレータを簡単に作成するための機能
ジェネレータは通常の関数のように記述するが、return のかわりに yield を使う
関数 next() を呼び出すたびに yield で指定したデータが返される
ジェネレータが終了すると自動的に例外 StopIteration を送出する
ジェネレータはイテレータの一種なので for 文で使用することができる
ジェネレータは再帰呼び出しも可能
Python 3.3 からは yield from 文を使用できる
- yield from expr は expr を評価してジェネレータを生成する
- 生成したジェネレータが返す値をそのまま yield で返す
- yield from gen() は for x in gen(): yield x と同じになる
内包表記のように ( ) の中で for 文を記述するとジェネレータを生成する (これをジェネレータ式という)
モジュール itertools にはイテレータを生成する便利な関数が用意されている
詳しくはライブラリリファレンスマニュアル 10.1. itertools を参照
>>> def triangular(n):
... x = 0
... for y in range(1, n + 1):
... x += y
... yield x
...
>>> for n in triangular(10): print(n)
...
1
3
6
10
15
21
28
36
45
55
>>> list(triangular(20))
[1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91, 105, 120, 136, 153, 171, 190, 210]
>>> def make_fibo(a = 0, b = 1):
... while True:
... yield a
... a, b = b, a + b
...
>>> fibo = make_fibo()
>>> fibo
<generator object make_fibo at 0x0000026047584BA0>
>>> next(fibo)
0
>>> next(fibo)
1
>>> next(fibo)
1
>>> next(fibo)
2
>>> next(fibo)
3
>>> next(fibo)
5
>>> next(fibo)
8
>>> g = (x * x for x in range(1, 10))
>>> g
<generator object <genexpr> at 0x0000026047584BF8>
>>> list(g)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> list(x for x in range(1, 20) if x % 2 == 0)
[2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> tuple(x for x in range(1, 20) if x % 2 != 0)
(1, 3, 5, 7, 9, 11, 13, 15, 17, 19)
●簡単なプログラム (1)
#
# sample01.py : 簡単なプログラム (1)
#
# Copyright (C) 2018 Makoto Hiroi
#
#
# FizzBuzz
#
def check(n):
if n % 15 == 0: return 'FizzBuzz'
if n % 3 == 0: return 'Fizz'
if n % 5 == 0: return 'Buzz'
return n
def fizzbuzz():
for x in range(1, 101):
print(check(x), end=' ')
def fizzbuzz1():
return map(check, range(1, 101))
#
# 数値積分 (中点則で円周率を求める)
#
def midpoint(n):
w = 1.0 / n
s = 0.0
for i in range(1, n + 1):
x = (i - 0.5) * w
s += 4.0 / (1.0 + x * x)
return s * w
#
# 平均値と標準偏差
#
import math
# 乱数で生成した身長のデータ
height = [
148.7, 149.5, 133.7, 157.9, 154.2, 147.8, 154.6, 159.1, 148.2, 153.1,
138.2, 138.7, 143.5, 153.2, 150.2, 157.3, 145.1, 157.2, 152.3, 148.3,
152.0, 146.0, 151.5, 139.4, 158.8, 147.6, 144.0, 145.8, 155.4, 155.5,
153.6, 138.5, 147.1, 149.6, 160.9, 148.9, 157.5, 155.1, 138.9, 153.0,
153.9, 150.9, 144.4, 160.3, 153.4, 163.0, 150.9, 153.3, 146.6, 153.3,
152.3, 153.3, 142.8, 149.0, 149.4, 156.5, 141.7, 146.2, 151.0, 156.5,
150.8, 141.0, 149.0, 163.2, 144.1, 147.1, 167.9, 155.3, 142.9, 148.7,
164.8, 154.1, 150.4, 154.2, 161.4, 155.0, 146.8, 154.2, 152.7, 149.7,
151.5, 154.5, 156.8, 150.3, 143.2, 149.5, 145.6, 140.4, 136.5, 146.9,
158.9, 144.4, 148.1, 155.5, 152.4, 153.3, 142.3, 155.3, 153.1, 152.3,
]
# 平均値と標準偏差
def meansd(xs):
m = sum(xs) / len(xs)
s = 0.0
for x in xs:
s += (x - m) * (x - m)
return m, math.sqrt(s / len(xs))
# 1 回の読み込みで求める
def meansd2(xs):
m, s = 0.0, 0.0
k = len(xs)
for i in range(1, k + 1):
x = xs[i - 1] - m
m += x / i
s += (i - 1) * x * x / i
return m, math.sqrt(s / k)
#
# パスカルの三角形
#
# 2 次元配列版
def pascal(n):
table = [[] for _ in range(n + 1)]
table[0].append(1)
table[1].append(1)
table[1].append(1)
for i in range(2, n + 1):
table[i].append(1)
for j in range(1, i):
table[i].append(table[i - 1][j - 1] + table[i - 1][j])
table[i].append(1)
for i in range(n + 1):
print(table[i])
# 一次元配列版
def pascal1(n):
table = [1] * (n + 1)
print([1])
for i in range(1, n + 1):
for j in range(i - 1, 0, -1):
table[j] += table[j - 1]
print(table[:i+1])
#
# 素数
#
# 単純版
def is_prime(n, ps):
for p in ps:
if p * p > n: break
if n % p == 0: return False
return True
def prime(n):
ps = [2]
for x in range(3, n + 1, 2):
if is_prime(x, ps):
ps.append(x)
return ps
# エラトステネスの篩
def sieve(n):
ps = [2]
xs = list(range(3, n + 1, 2))
while True:
p = xs[0]
if p * p > n: break
ps.append(p)
xs = list(filter(lambda x: x % p != 0, xs))
return ps + xs
# 高速版
def sieve1(n):
ps = [True] * ((n + 1) // 2 - 1)
x = 3
while x * x <= n:
y = (x - 3) // 2
if ps[y]:
y += x
while y < len(ps):
ps[y] = False
y += x
x += 2
return [2] + [i * 2 + 3 for i, x in enumerate(ps) if x]
#
# 素因数分解
#
def factorization(n):
def factor_sub(n, m):
c = 0
while n % m == 0:
c += 1
n //= m
return c, n
#
buff = []
c, m = factor_sub(n, 2)
if c > 0: buff.append((2, c))
c, m = factor_sub(m, 3)
if c > 0: buff.append((3, c))
x = 5
while m >= x * x:
c, m = factor_sub(m, x)
if c > 0: buff.append((x, c))
if x % 6 == 5:
x += 2
else:
x += 4
if m > 1: buff.append((m, 1))
return buff
#
# 順列
#
def permutations(n, xs, func):
def perm(ps):
if len(ps) == n:
func(tuple(ps))
else:
for x in xs:
if x in ps: continue
ps.append(x)
perm(ps)
ps.pop()
perm([])
# ジェネレータ版
def make_perm(n, xs):
if n == 0:
yield []
else:
for ys in make_perm(n - 1, xs):
for x in xs:
if x in ys: continue
yield ys + [x]
# 重複順列
def repeat_perm(n, xs, func):
def perm(ps):
if len(ps) == n:
func(tuple(ps))
else:
for x in xs:
ps.append(x)
perm(ps)
ps.pop()
perm([])
#
# 組み合わせ
#
# 二重再帰
def combination_number(n, r):
if r == 0 or n == r:
return 1
else:
return combination_number(n - 1, r) + combination_number(n - 1, r - 1)
# 末尾再帰
def combination_number1(n, r):
if n == 0 or r == 0: return 1
return combination_number1(n, r - 1) * (n - r + 1) // r
# 組み合わせ
def combinations(n, xs, func):
def comb(i, a):
if len(a) == n:
func(tuple(a))
elif i < len(xs):
a.append(xs[i])
comb(i + 1, a)
a.pop()
comb(i + 1, a)
comb(0, [])
# ジェネレータ版
def make_comb(n, xs, i = 0):
if n == 0:
yield []
elif i < len(xs):
for ys in make_comb(n - 1, xs, i + 1):
yield [xs[i]] + ys
for ys in make_comb(n, xs, i + 1):
yield ys
# 重複組み合わせ
def repeat_comb(n, xs, func):
def comb(i, a):
if len(a) == n:
func(tuple(a))
elif i < len(xs):
a.append(xs[i])
comb(i, a)
a.pop()
comb(i + 1, a)
comb(0, [])
if __name__ == '__main__':
print("----- FizzBuzz -----")
fizzbuzz()
print("")
print(list(fizzbuzz1()))
print("----- 数値積分 -----")
print(midpoint(10000))
print("---- 平均値と標準偏差 -----")
print(meansd(height))
print(meansd2(height))
print("----- パスカルの三角形 -----")
pascal(15)
pascal1(15)
print("----- 素数 -----")
print(prime(100))
print(sieve(200))
print(sieve1(300))
print("----- 素因数分解 -----")
print(factorization(24))
print(factorization(12345678))
print(factorization(1234567890))
print(factorization(11111111111))
print("----- 順列 -----")
permutations(4, [1,2,3,4], print)
print(list(make_perm(4, [1,2,3,4])))
print("----- 重複順列 -----")
repeat_perm(3, [1,2,3], print)
print("----- 組み合わせの数 -----")
print(combination_number(22, 11))
print(combination_number1(26, 13))
print("----- 組み合わせ -----")
combinations(3, [1,2,3,4,5], print)
print(list(make_comb(3, [1,2,3,4,5])))
print("----- 重複組み合わせ -----")
repeat_comb(3, [1,2,3,4,5], print)
----- FizzBuzz -----
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz
Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz
41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz Fizz 52 53 Fizz Buzz 56 Fizz 58 59 FizzBuzz
61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz 76 77 Fizz 79 Buzz
Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz Buzz
[1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7, 8, 'Fizz', 'Buzz', 11, 'Fizz', 13, 14, 'FizzBuzz',
16, 17, 'Fizz', 19, 'Buzz', 'Fizz', 22, 23, 'Fizz', 'Buzz', 26, 'Fizz', 28, 29,
'FizzBuzz', 31, 32, 'Fizz', 34, 'Buzz', 'Fizz', 37, 38, 'Fizz', 'Buzz', 41, 'Fizz',
43, 44, 'FizzBuzz', 46, 47, 'Fizz', 49, 'Buzz', 'Fizz', 52, 53, 'Fizz', 'Buzz', 56,
'Fizz', 58, 59, 'FizzBuzz', 61, 62, 'Fizz', 64, 'Buzz', 'Fizz', 67, 68, 'Fizz', 'Buzz',
71, 'Fizz', 73, 74, 'FizzBuzz', 76, 77, 'Fizz', 79, 'Buzz', 'Fizz', 82, 83, 'Fizz',
'Buzz', 86, 'Fizz', 88, 89, 'FizzBuzz', 91, 92, 'Fizz', 94, 'Buzz', 'Fizz', 97, 98,
'Fizz', 'Buzz']
----- 数値積分 -----
3.141592654423134
---- 平均値と標準偏差 -----
(150.62699999999998, 6.433472701426501)
(150.62699999999998, 6.433472701426506)
----- パスカルの三角形 -----
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
[1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1]
[1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1]
[1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1]
[1, 13, 78, 286, 715, 1287, 1716, 1716, 1287, 715, 286, 78, 13, 1]
[1, 14, 91, 364, 1001, 2002, 3003, 3432, 3003, 2002, 1001, 364, 91, 14, 1]
[1, 15, 105, 455, 1365, 3003, 5005, 6435, 6435, 5005, 3003, 1365, 455, 105, 15, 1]
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
[1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1]
[1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1]
[1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1]
[1, 13, 78, 286, 715, 1287, 1716, 1716, 1287, 715, 286, 78, 13, 1]
[1, 14, 91, 364, 1001, 2002, 3003, 3432, 3003, 2002, 1001, 364, 91, 14, 1]
[1, 15, 105, 455, 1365, 3003, 5005, 6435, 6435, 5005, 3003, 1365, 455, 105, 15, 1]
----- 素数 -----
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79,
83, 89, 97]
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79,
83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
179, 181, 191, 193, 197, 199]
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79,
83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269,
271, 277, 281, 283, 293]
----- 素因数分解 -----
[(2, 3), (3, 1)]
[(2, 1), (3, 2), (47, 1), (14593, 1)]
[(2, 1), (3, 2), (5, 1), (3607, 1), (3803, 1)]
[(21649, 1), (513239, 1)]
----- 順列 -----
(1, 2, 3, 4)
(1, 2, 4, 3)
(1, 3, 2, 4)
(1, 3, 4, 2)
(1, 4, 2, 3)
(1, 4, 3, 2)
(2, 1, 3, 4)
(2, 1, 4, 3)
(2, 3, 1, 4)
(2, 3, 4, 1)
(2, 4, 1, 3)
(2, 4, 3, 1)
(3, 1, 2, 4)
(3, 1, 4, 2)
(3, 2, 1, 4)
(3, 2, 4, 1)
(3, 4, 1, 2)
(3, 4, 2, 1)
(4, 1, 2, 3)
(4, 1, 3, 2)
(4, 2, 1, 3)
(4, 2, 3, 1)
(4, 3, 1, 2)
(4, 3, 2, 1)
[[1, 2, 3, 4], [1, 2, 4, 3], [1, 3, 2, 4], [1, 3, 4, 2], [1, 4, 2, 3], [1, 4, 3, 2],
[2, 1, 3, 4], [2, 1, 4, 3], [2, 3, 1, 4], [2, 3, 4, 1], [2, 4, 1, 3], [2, 4, 3, 1],
[3, 1, 2, 4], [3, 1, 4, 2], [3, 2, 1, 4], [3, 2, 4, 1], [3, 4, 1, 2], [3, 4, 2, 1],
[4, 1, 2, 3], [4, 1, 3, 2], [4, 2, 1, 3], [4, 2, 3, 1], [4, 3, 1, 2], [4, 3, 2, 1]]
----- 重複順列 -----
(1, 1, 1)
(1, 1, 2)
(1, 1, 3)
(1, 2, 1)
(1, 2, 2)
(1, 2, 3)
(1, 3, 1)
(1, 3, 2)
(1, 3, 3)
(2, 1, 1)
(2, 1, 2)
(2, 1, 3)
(2, 2, 1)
(2, 2, 2)
(2, 2, 3)
(2, 3, 1)
(2, 3, 2)
(2, 3, 3)
(3, 1, 1)
(3, 1, 2)
(3, 1, 3)
(3, 2, 1)
(3, 2, 2)
(3, 2, 3)
(3, 3, 1)
(3, 3, 2)
(3, 3, 3)
----- 組み合わせの数 -----
705432
10400600
----- 組み合わせ -----
(1, 2, 3)
(1, 2, 4)
(1, 2, 5)
(1, 3, 4)
(1, 3, 5)
(1, 4, 5)
(2, 3, 4)
(2, 3, 5)
(2, 4, 5)
(3, 4, 5)
[[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5],
[2, 4, 5], [3, 4, 5]]
----- 重複組み合わせ -----
(1, 1, 1)
(1, 1, 2)
(1, 1, 3)
(1, 1, 4)
(1, 1, 5)
(1, 2, 2)
(1, 2, 3)
(1, 2, 4)
(1, 2, 5)
(1, 3, 3)
(1, 3, 4)
(1, 3, 5)
(1, 4, 4)
(1, 4, 5)
(1, 5, 5)
(2, 2, 2)
(2, 2, 3)
(2, 2, 4)
(2, 2, 5)
(2, 3, 3)
(2, 3, 4)
(2, 3, 5)
(2, 4, 4)
(2, 4, 5)
(2, 5, 5)
(3, 3, 3)
(3, 3, 4)
(3, 3, 5)
(3, 4, 4)
(3, 4, 5)
(3, 5, 5)
(4, 4, 4)
(4, 4, 5)
(4, 5, 5)
(5, 5, 5)