python3 プログラミング勉強会
TRANSCRIPT
Python3 プログラミング
2014-07-01 Tetsuya Morimoto
このスライドの注意事項/意図● 基本的に Python3 を前提にこのスライドを作っています● Python3 の特徴について網羅できてはいません
– ググればすぐに見つかるようなものは省いた● 文字列が全て Unicode になった● 組み込み関数がジェネレーターを返すようになった● 標準モジュール名やそのメソッドの命名規則が統一された
– Python2 から 3 への移行のノウハウはありません– Python2 と 3 の両対応のノウハウはありません
Python3 でプログラミングするときに知っておくと楽しそうなことを自分なりに調べてきたがんばって調べたけど、間違ってたらごめんなさい
概要● きっかけ● Python2 vs Python3● Python パッケージング● (IMHO) Python プログラミング
– Python のイディオム● まとめ
きっかけ
unotools (1)● unotools ( ゆーのつーるず )
– Python3 のみ対応– OpenOffice.org/LibreOffice の UNO ( ゆーの ) を簡
単に使うためのライブラリ● PyUNO のラッパーライブラリ
– OpenDocument 形式を Python から簡単に扱う– まだアルファレベルだけど簡単なことはできる
● 初めて Python3 でコードを書いたら楽しかった
この勉強会を開こうと思ったきっかけ
unotools (2)● 使い方/用途
– バッチ処理でのレポート生成– OpenDocument から他のファイル形式へのフォーマット変換
● 画像イメージ、 html 、 pdf など
$ soffice --accept='socket,host=localhost,port=8100;urp;StarOffice.Service'
$ python sample-scripts/calc-sample1.py -s localhost -d sample-scripts/datadir/$ lssample-calc.html sample-calc.pdf sample-calc_html_eaf26d01.pngsample.csv sample.xls
Python2 vs Python3
どちらの Python バージョンを使ったら良い?
● 自分が選べるなら Python3 を選べば良い● どっちを選んで良いのか分からなかったら
Python3 を選べば良い● 誰かが Python3 の移行が進んでないという
理由で Python2 を使おうと言ってたのなら
それは無視すれば良い
Python2 を使わないといけない理由がないなら普通に Python3 を選べば良いと思う
但し Linux Distribution の事情は悩ましい
● クライアント向け– Ubuntu 14.04 LTS
● Python 2.7.6, Python 3.4.0– Fedora 20
● Python 2.7.5, Python 3.3.2
● サーバー向け– RHEL/CentOS 5.x ( サポート 2017 年 3 月 31 日まで )
● Python 2.4.x● EPELが使えるならPython 2.6● IUS Community Projectが使えるならPython2.7, 3.1
– RHEL/CentOS 6.x ( サポート 2020 年 11 月 30 日まで )● Python 2.6.x● IUS Community Projectが使えるならPython2.7, 3.2, 3.3
– RHEL/CentOS 7.x ( サポート 2024 年 6 月 30 日まで )● Python 2.7.x
Python3 もインストールできるけど、依然として Python2 も残っているし、デフォルトは 2 を使うようになってる
RHEL/CentOS は 2024 年まで Python2 が残るPython3 への移行を阻害している 1 つの要因でもある
でも、これは大した問題ではない
標準リポジトリでは提供されないためPython3 を RPM で管理したいなら外部リポジトリを使わないといけない
移行においては悩ましい課題
PEP373 コミュニティによるPython2.7 のサポートは 2020 年まで
別解 Python Distribution という概念
● Using the Anaconda Python Distribution– Conda: Python-agnostic binary package manager
● Python はマルチプラットフォームに対応している● Conda はクロスプラットフォームなパッケージマネージャー● 複数の Python バージョンを切り替えたり、仮想環境にも対応
● Conda は Anaconda (RHEL のインストーラー ) で使われるパッケージマネージャーを単体ツールとして切り出したもの
– Continuum 社が Anaconda distributionを提供 ( 商用サービスもある )● PyPI にあるパッケージをミラーリングして Conda パッケージとして配布● バイナリディストリビューションとしても使える
– Python2.7 または 3.4 を内包した MinicondaをWindows, MacOSX, Linux 向けに提供
Python 製のシステム向けディストリビューションを配布する
誤解を恐れずに言えば、パッケージマネージャーをインストールするついでに Python もインストールしちゃってそれはユーザー ( システム管理者 ) から見えないから Conda 内で開発者が管理できるConda 内で Python インタープリターは八百万の神様みたいな存在
agnostic( 不可知論 )Conda は内部的に Python を使うけど、Conda から Python は見えないし意識する必要もない
Pythonパッケージング
Python のパッケージマネージャー
That's a shame
いままではそうだったんだけど、Python3.4 から pip がデフォルトインストーラーになった
https://twitter.com/gardaud/status/357638468572151808
pip● PEP453で pip が Python(3.4 から ) 本体に含まれ
るようになった– --user オプションでホーム配下にパッケージをインス
トールできる● pip は厳密に言うとパッケージインストーラー
– easy_install の不評● setuptools に含まれるパッケージインストーラー● 初期のバージョンの頃から機能に対する悪評● アンインストール機能の欠如
– もともとは easy_install の置き換えを目的として発展
コラム ~バージョンニング~● Python
– major.minor.micro: 3.4.0– PEP440 (PEP426から派生 )
● [N:]N(.N)*[{a|b|c|rc}N][.postN][.devN]● Author: Nick Coghlan, Status: Draft● Epochはバージョンスキームが変わったときに使う● Source labelは依存性解決などのツールのために使う
● 他のも見てみる– Semantic Versioning: major.minor.patch– Linux Kernel: major.minor.revision(patch) => 3.13.0-24– Ruby: major.minor.teeny-patch => 1.9.3p545
ちょっと凝り過ぎ?
3 番目のバージョン番号の呼び方はコミュニティによって違う
パッケージング/パッケージ配布 (1)
● 2009 年頃から 2012 年頃までの事情– パッケージングのこと知りたかったら清水川Webへ– 2009/12/05 distutils, setuptools, distribute, pip, virtualenv, buildout
● この頃を簡単に言うと ...– setuptools を多くの開発者が使うようになってた– ↑ メンテナンスされなくなって置き換えツールが乱立– 「標準化へ向けて俺たちの戦いはこれからだ」だった
ややこしいからね
パッケージング/パッケージ配布 (2)● 歴史的経緯
– 1998 標準ライブラリとしてdistutilsを提供– 2000 distutilsの開発を中止
● この後、開発者がdistutils を使って自分で開発し始める– 2004 setuptoolsの開発が始まる
● 当時は開発が活発だった● 依存関係の解決● Eggによるバイナリパッケージ配布
– 2009 distributeの開発をTarek Ziadéが始める● メンテナンスされなくなったsetuptoolsを置き換えることが狙い● setuptools の完全互換性を保持してメンテナンス再開、Python3対応も始める
– 2012 distutils2の開発を中止● Python3.3でpackagingとして標準ライブラリ化予定だったが、中止になった
– distutils と同じ欠陥があった、コマンドベースの設計がいけてないとか– Windows向けのネイティブスクリプト実行やエントリーポイントの定義などが未サポート
– 2013 [Distutils] Setuptools-Distribute merge announcement● Setuptools とdistributeの統合を発表 (=> distributeの開発を中止 )● プロジェクトのゴールはsetuptoolsが "Obsolete" になること● 次世代パッケージングの何かへ移行するまではメンテナンスを継続
– 2014 setuptools再び開発が活性化● Rapid Releaseな開発サイクルへ
成功を収めた
packaging は放棄されたけど、distutils を置き換える distlibへ
setuptools で解決できてた
setuptools のリリース履歴setuptools 0.6c1 2006-07-20setuptools 0.6c5 2007-01-09setuptools 0.6c8 2008-02-15setuptools 0.6c9 2008-09-24
Setuptools-Distribute merge announcement 2013-03-14
setuptools 0.6.41 2013-05-24 (distribute マージ )setuptools 0.7 2013-06-02setuptools 1.0 2013-08-18 ( 後方互換性を放棄 )setuptools 2.0 2013-12-08setuptools 3.0 2014-03-04setuptools 4.0 2014-06-02setuptools 5.0 2014-06-15setuptools 5.2 2014-06-23 ( 最新 )
Python2.4
Python2.0
Python2.7, 3.3
Python2.6, 3.0
Python2.7, 3.4
setuptools の原罪と 罪贖
パッケージング/パッケージ配布 (3)● パッケージングと pbr
– Python Build Reasonableness が名前の由来– setuptools を拡張するツール
● ビルドとデプロイを簡単にすることが目的– git tag やリビジョンからバージョンを自動生成– git log から AUTHORS と ChangeLog を自動生成– スマートなパッケージファイル探索– requiremetns.txt から依存関係を解決– Sphinx でドキュメントを生成
● distutils2 由来のパッケージのメタデータを setup.cfg として使う– 宣言型の設定ができる
● テストランナーに toxと testrを使う– tox はテストのために仮想環境を構築して、インストール、依存関係の解決、テストを実行– testr はテスト結果を追跡するためのツール (testrepository のテストランナー )
● tox で作った仮想環境に入って失敗したテストを再現させたり、デバッグしたりできるらしい– OpenStackプロジェクトがメンテナンスしてるから安心?
git と連携したパッケージ作成
PEP426 Metadata 2.0フォーマットを定義
パッケージング/パッケージ配布 (4)● そしてwheel フォーマットへ
– Python の公式ディストリビューションフォーマットがなかった– setuptools が導入した egg フォーマットはあったが ...
● zip アーカイブ内にパッケージ情報が付いてる● pip が egg を扱えない ( これまでは sdist のみだった )● egg はインポート可能なフォーマット
– PEP427で公式フォーマットwheel を定義● やっぱりアーカイブは zip フォーマット● pip でも扱える● whl はインストールのためのフォーマット
– パッケージングのこと知りたかったら@aodagへ● パッケージングの今と未来
– Python2.x/3.x 両対応のパッケージングとか● Python 2.5 からPython 3.3 で 動作するツールの作り方
Python のパッケージングでググったらこの 2 人がヒットする
拡張子は .whl
Metadata 2.0 やパッケージ構造がPEP のそれと非互換?
java の jar ファイルのようなもの?
コラム ~メンテナンス~● 自分が作ったパッケージが世界中で広く使われるようになった
– 最初のうちは やがてメンテナンスに� �
– たびたびそういうのは発生する● setuptools 然り、 MySQL-Python 然り
● Python 3 Readiness– MySQL-Python も Python3 対応が望まれていた ...
● Python_3_の_MySQL_ドライバ事情● MySQL-python の Python 3 対応● mysqlclient をリリースしました
● メンテナーに権限を移譲しよう– 1 人でずっと OSS をメンテナンスするのはしんどい
(IMHO)Python プログラミング
Python が大事にしている概念● TOOWTDI (There's Only One Way To Do IT)
– やり方はたった 1 つでいい● Pythonic とは
– 小さなコードパターンに最も効率的なイディオムを使ったコーディングや構文– Code Like a Pythonista: Idiomatic Python– The Hitchhiker’s Guide to Python!
● The Zen of Python (PEP20)– Beautiful is better than ugly. (汚いよりきれい方が良い )– Explicit is better than implicit. (暗黙的よりも明示的な方が良い )– Although practicality beats purity. ( とはいえ、純粋さよりも実用的であること )
● The number one thing that Python programmers do is read code.– コードは書かれるよりも読まれることの方が多い
そんな Python であっても、コーディングやそのイディオムは同じであっても、設計の在り方は個々のプログラマーによって違う人それぞれに設計スタイルが違うの当然で、全体で一貫性があれば良いと思う
Python のイディオムを知ろう
他人のコードを読んでいて気になるのは Pythonic じゃないコード、つまりはそのイディオムが共有されてないとき
PEP8 の一節"A Foolish Consistency is the Hobgoblin of Little Minds"
また、このバランスも難しい
デコレーター● 動的に関数やクラスを拡張するための構文
– GoF の Decorator patternとは厳密には違うみたい– 関数ラッピングの可読性を上げる狙い– 簡潔に書けるからよく使われる
● デコレートの用途– 引数チェック– キャッシュ– プロキシ– コンテキストプロバイダー
但し、デコレーターでラップする関数の返り値を変えたりするのはよくない
PythonDecoratorsにサンプルがたくさんある
functools.singledispatch● PEP443 Single-dispatch generic functions
– プロキシデコレーターのひとつ– Common Lisp Object System から触発?– 汎用関数を作るための仕組み
サンプルコードfrom functools import singledispatch
@singledispatchdef judge(type_): raise NotImplementedError('unknown type: {}'.format(type_))
@judge.register(int)def int_type(num): print('I am integer: {}'.format(num))
@judge.register(list)def list_type(sequence): print('I am list: {}'.format(sequence))
def main(): judge(1) judge([2, 3]) judge('test')
汎用関数 judge の第一引数の型でディスパッチ
# これまでのパターン# 汎用関数に isinstance を使うdef judge(data): if isinstance(data, int): return int_type(data) elif isinstance(data, list): return list_type(data) else: raise NotImplementedError(...)
functools.lru_cache● @functools.lru_cache(maxsize=128, typed=False)
– 関数の結果を LRU (latest recently used) キャッシュで保持するデコレーター
– メモ化などに使うと便利
サンプルコードfrom functools import lru_cache
@lru_cache(maxsize=None)def memoize_fib(n): """ >>> [memoize_fib(n) for n in range(16)] [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610] >>> memoize_fib.cache_info() CacheInfo(hits=28, misses=16, maxsize=None, currsize=16) """ if n < 2: return n return memoize_fib(n-1) + memoize_fib(n-2)
In [1]: from lru_cache_sample1 import fib, memoize_fibIn [2]: timeit -n 3 [fib(n) for n in range(20)]3 loops, best of 3: 6.49 ms per loopIn [3]: timeit -n 3 [memoize_fib(n) for n in range(20)]3 loops, best of 3: 36.2 µs per loop
IPython を使うとパフォーマンスを比較するのが簡単
with 文と contextlib● PEP343 The "with" statement
– Lisp でよく使われた with-* マクロから触発?– コンテキストマネージャーと一緒に使う
● contextlibを使うと便利● グローバル情報の保存/更新● リソースのロック/アンロック● ファイルのオープン/クローズ
with open('test.txt') as f: first, *rest, last = f.readlines()
f = open('test.txt')try: first, *rest, last = f.readlines()finally: f.close()
サンプルコード
from contextlib import contextmanager
@contextmanagerdef my_open(filename): f = open(filename) try: yield f finally: f.close()
class MyContextManager: def __init__(self, filename): self.filename = filename self.file = None
def __enter__(self): self.file = open(self.filename) return self.file
def __exit__(self, type, value, traceback): if type is not None: self.file.close()
コンテキストマネージャの定義
ジェネレーター● イテレーターやコルーチンを実装するための仕組み● itertoolsはイテレーターを扱ったり、
ジェネレーターを作るツールのライブラリ– APL, Haskell, SML から触発されてる– 高速、省メモリ、 iterable を受け取ってイテレー
ターを返す
サンプルコードdef read_file(filename): """ ファイルを1行ずつ読み込んで返す >>> list(read_file('test.txt')) ['1', '2', '3', '4', '5'] """ with open(filename) as f: for line in f: yield line.strip()
def read_file_with_int(filename): """ >>> list(read_file_with_int('test.txt')) [1, 2, 3, 4, 5] """ return map(int, read_file(filename))
def read_file_with_int_gc(filename): """ >>> list(read_file_with_int_gc('test.txt')) [1, 2, 3, 4, 5] """ return (int(x) for x in read_file(filename))
ジェネレーターを作成するには yield を使うだけ
イテレーターと高階関数を組み合わせると簡潔になる?
def read_file_with_power(filename, num): """ >>> from itertools import repeat >>> list(map(pow, read_file_with_int('test.txt'), repeat(3))) [1, 8, 27, 64, 125] >>> list(read_file_with_power('test.txt', 3)) [1, 8, 27, 64, 125] """ from itertools import repeat return map(lambda x, y: pow(int(x), y), read_file(filename), repeat(num))
map とジェネレーター内包表記どちらでも好みで良い
ジェネレーターを使った移譲● PEP380 Syntax for Delegating to a Subgenerator
– PEP342で yield を式として扱えるようになった● send: ジェネレーターに値を渡す● throw: ジェネレーターに例外を発生させる● close: ジェネレーターを終了させる
– これらをサブジェネレーターで扱うのが大変
サンプルコード
def read_file_with_end(filename): """ >>> list(read_file_with_end('test.txt')) ['1', '2', '3', '4', '5', 'end'] """ for line in read_file(filename) yield line yield 'end'
def read_file_with_type(filename): """ >>> g = read_file_with_type('test.txt') >>> next(g) 'start' >>> g.send(int) 1 """ type_ = yield 'start' with open(filename) as f: for line in f: type_ = yield type_(line.strip())
ジェネレーターで値を生成するだけなら移譲は簡単
yield from を使うと値が渡せるジェネレーターへの移譲も簡単
def read_file_with_send(filename): """ >>> g = read_file_with_send('test.txt') >>> next(g) 'start' >>> list(map(g.send, [int, str, list, tuple, bool])) [1, '2', ['3'], ('4',), True] >>> next(g) 'end' """ yield from read_file_with_type(filename) yield 'end'
Python の保守的コーディング● つい後方互換性を気にしてコードを書きがち
– Python2.4 (RHEL/CentOS 5.x があると辛い ) はオワコン● functools や with 文と contextlib は Python2.4 で使えない● ジェネレーターは yield 文のみ
– try … finally から yield 使えない● try … except … finally も書けない● operator.itemgetter/attrgetter で複数の引数を取れない● r = True if condition else False も書けない
– 小さなコードパターンを扱うライブラリは揃ってきているが、 2.4 を考慮して使えないことが多い
● Python3 はこれらが使い放題 → 書いてて楽しい
他にも 2.4 でできないことがある2.4 のことを考えながらコーディングするのはもううんざり
その次は 2.6 で、これも直に辛くなる ...
データ構造とアルゴリズム● 集合型 set の応用例
– 集合演算使うと処理が簡潔になることがあるdef has_invalid_fields_loop(fields): for field in fields: if field not in ['foo', 'bar']: return True return False
def has_invalid_fields_set(fields): return bool(set(fields) - set(['foo', 'bar']))
ループがなくなった!上の方が意図は分かりやすい?下の方がコードが簡潔?
super を使った処理の移譲● Python Is Not Java
– ここにある話題じゃないけど、多重継承もその 1 つ● Python’s super() considered super!
– super はスーパーだという話– Python の多重継承は移譲の仕組みを提供
● MRO(Method Resolution Order): C3線形化アルゴリズム● MRO の仕組みを知っていれば後から移譲先を変更できる● 既存のコードをいじらない → コードが安定する
サンプルコード>>> class A:... def f(self):... print('A')
>>> class B(A):... def f(self):... print('B')... super().f()
>>> class C(B): pass>>> C.mro()[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]>>> C().f()BA
>>> class D(A):... def f(self):... print('D')
>>> class E(C, D): pass>>> E.mro()[<class '__main__.E'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>]>>> E().f()BD B.f メソッドで呼び出す
super が D.f に変更された!
A
B
C
D
E1
3
2
4
5
object
6
コラム 〜 unicode 文字列〜● 文字列が全部 Unicode になった
– 見た目が文字の Unicode は変数として使える
>>> π = 3.14
>>> λ = lambda x: x ** 2
javaから学んだもの
concurrent.futures● PEP 3148: The concurrent.futures module
– java.util.concurrent から触発されたモジュール– Executor でスレッド/プロセス実行を制御する
高レベルインターフェースを提供
enum● PEP435 Adding an Enum type to the Python s
tandard library– これも java から触発?と思ったけど違うかも?– enumのドキュメントを読んでください
まとめ● Python3 すごいよ、楽しいよ
– 後方互換性を気にせずコーディングできる楽しさ– 小さなコードパターンを扱う言語機能や標準ライブ
ラリが整ってきて、関数型プログラミングっぽい簡潔なコードが書きやすくなった
– Python3 対応のライブラリも十分揃ってる● これからは Python3 を使おう
中級者向け● このスライドの内容が面白かったら ...
– The Hacker's Guide to Python ( 英語 ) 読むと良い● 2014-03 に出版されたばかりだから内容が新しい● 本スライドでも多くの内容を参考にして引用しました
コーディング前にドキュメントを書くというのは、実装の詳細をコミットしないでその機能やワークフローをじっくり考え抜くことになり得る、というのも分かった
https://julien.danjou.info/books/the-hacker-guide-to-python
日本語で技術書を読みたい人向け● 英語を読むのが辛かったら ...
– エキスパート Python プログラミングを買うと良い● 原著 2008-09 出版、 2010-05-31 翻訳● 基本的に Python2.x 向けなのでさすがに内容は古い
– それでも Python でのアプリ開発の要点は掴める– 付録 A の Unicode の章は本書オリジナルでよくはまるところ
● 日本で初めて発売された Python 中級者向けの技術書– この本が売れれば The Hacker's Guide to Python が翻訳される可能性も高くなる?
– 読まなくても買うと未来につながるかも?http://ascii.asciimw.jp/books/books/detail/978-4-04-868629-7.shtml
ステマ
半分冗談だよ!
初心者向け● このスライドの内容が難しかったら ...
– パーフェクト Python 読むと良い● Python の禅を全て解説している● 言語仕様、ライブラリ、応用範囲まで一通り網羅
http://gihyo.jp/book/2013/978-4-7741-5539-5
ステマ
Python は比較的に言語仕様がシンプルで,学習が容易です。( 中略 )今年はへび年なので,こっそりと Python が日常に忍び込んで欲しいですね。
その他に参考にしたもの● そうだ!EuroPython2011へ行こう● 「PyCon US 2012」 イベントレポート● Key differences between Python 2.7.x and Py
thon 3.x● 10 awesome features of Python that you can'
t use because you refuse to upgrade to Python 3