Python3 プログラミング勉強会の補足

先日、Python3 プログラミング勉強会 を開催しました。

以下に資料の中で引用したサンプルコードを置いておきました。

ちなみにこういったサンプルコードをアップする前に pep8 と flakes と doctest のテストを実行しておけば typo してツッコミを受けることもありません。例えば pytest 使えば、以下のように簡単にできます。

$ py.test --pep8 --flakes --doctest-module --clear . 
============================= test session starts ==============================
platform linux -- Python 3.4.0 -- py-1.4.20 -- pytest-2.5.2
plugins: flakes, pep8, cache
collected 39 items 

algorithm_set1.py ....
generator_sample1.py ...
generator_sample2.py ......
generator_sample3.py .......
lru_cache_sample1.py ....
singledispatch_sample1.py ....
super_sample1.py ....
test.txt .
with_sample1.py ......
========================== 39 passed in 0.15 seconds ===========================

ページ14: バージョニングのお話

勉強会ではスライドを飛ばしたけど、バージョニングのスキームがすごいことになってます。

[N:]N(.N)*[{a|b|c|rc}N][.postN][.devN]

RPM 以外で Epoch をバージョニングのスキームに組み込んでいるのを私は初めてみました。PEP440 の提案者が Nick Coghlan (@) で、プロフィールをみると Red Hat で働いている開発者だったので元ネタは RPM なのかもしれません。あと、Python の3番目のバージョンをマイクロと呼ぶのも知りませんでした。

ページ16: setuptools の原罪と贖罪

このスライド1枚作るのに何時間もかけて情が移ってしまったために、このスライドを力説し過ぎて時間を浪費してしまいました。この資料の中で一番価値のあるスライドだと思っているいるので、何かに困ったときに見返してください。

ページ17: パッケージングと pbr

あー、pbr の話ももっとしたかったです。これだけで勉強会しても良いぐらいです。OpenStack プロジェクトで何が起きているか、みたいなのは今後の Python の未来に大きな影響を与えるような気がします。

簡単に紹介すると、setup.py にパッケージ情報を書かずに setup.cfg に Metadata2.0 のフォーマットでパッケージ情報を記述できます。

$ git clone https://github.com/openstack-dev/pbr
$ cd pbr/
$ vi setup.py
import setuptools
from pbr import util

setuptools.setup(
    **util.cfg_to_args())
$ vi setup.cfg 
[metadata]
name = pbr
author = OpenStack
author-email = openstack-dev@lists.openstack.org
summary = Python Build Reasonableness
...

git の履歴から AUTHORS や ChangeLog も自動生成してくれます。git のコミットログから ChangeLog を作ってくれるのは結構便利かもしれませんね。コミットログの最初の行を ChangeLog のサマリとして扱ってるようにみえます。マージのコミットログは無視してたりして賢いです。

$ workon pbr3  # pbr や関連ツールをインストールしておいた仮想環境を使う
(pbr3)$ python setup.py sdist
(pbr3)$ head AUTHORS 
Alex Gaynor <alex.gaynor@gmail.com>
Andrew Bogott <abogott@wikimedia.org>
Angus Salkeld <asalkeld@redhat.com>
Anthony Young <sleepsonthefloor@gmail.com>
Attila Fazekas <afazekas@redhat.com>
Ben Nemec <bnemec@redhat.com>
Bhuvan Arumugam <bhuvan@apache.org>
Brian Waldon <bcwaldon@gmail.com>
Chang Bo Guo <guochbo@cn.ibm.com>
ChangBo Guo(gcb) <eric.guo@easystack.cn>
...

(pbr3)$ cat ChangeLog 
CHANGES                                                                        
=======                                                                        
                                                                               
* Allow examining parsing exceptions                                           
* Update integration script for Apache 2.4                                     
* Restore Monkeypatched Distribution Instance                                  
* Register testr as a distutil entry point                                     
* Check for git before querying it for a version                               
* Allow _run_cmd to run commands in any directory                              
* Make setUp fail if sdist fails                                               
* Permit pre-release versions with git metadata                                
* Un-nest some sections of code                                                
                                                                               
0.8.2                                                                          
-----                                                                          
                                                                               
* Remove --use-mailmap as it's not needed                                      
* Fix typos in docs                                                            
                                                                               
0.8.1                                                                          
-----                                                                          
                                                                               
* pbr/testr_command.py: Add logging                                            
* Documentation updates                                                        
* Fixed a typo in the documentation                                            
* Make source configurable when using --coverage                               
* README.rst: tweaks                                                           
* Format autoindex.rst file properly                                           
* make pbr use hacking directly   
...

(pbr3)$ git log
commit e1b98f578a0f94a791210dd48530e2fed43fbe61
Merge: fa17f42 6541911
Author: Jenkins <jenkins@review.openstack.org>
Date:   Mon Jun 30 17:34:07 2014 +0000

    Merge "Check for git before querying it for a version"

commit fa17f42d7d195dab77c042ac4881ac63bfb638f3
Author: Robert Collins <rbtcollins@hp.com>
Date:   Mon Mar 17 09:46:58 2014 +1300

    Allow examining parsing exceptions.
    
    Having to put breakpoints in pbr to diagnose issues is bad for dealing
    with reports from users.
    
    Change-Id: Ifecf4c4e4bb5955e0e5feb4bf5b5b85150b08ebe

commit d815366577a7dbb7a8dab66ab105e2803af4c007
Merge: ec1009c bdb0191
Author: Jenkins <jenkins@review.openstack.org>
Date:   Fri Jun 27 02:29:12 2014 +0000

    Merge "Un-nest some sections of code"

commit ec1009cf197ed555b29b6f226adb300914ea5bd0
Author: Sean Dague <sean@dague.net>
Date:   Thu Jun 26 08:22:47 2014 -0400

    Update integration script for Apache 2.4
    
    Apache 2.4 requires site configuration files to have a ".conf"
    extension, and Apache 2.2 does not want the extension. Add logic to
    figure out the right name for the file so we can run the tests against
    both versions of Apache.
    
    Fixes bug: #1334326
...

テストランナーに tox と testr を使います。setuptools の拡張コマンドに testr オプションが追加されています。デフォルトでテストを並列実行 (CPU の数かな?) してくれるようです。

(pbr3)$ python setup.py testr
running testr
running=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_TEST_TIMEOUT=60 ${PYTHON:-python} -m subunit.run discover -t ./ . --list 
running=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_TEST_TIMEOUT=60 ${PYTHON:-python} -m subunit.run discover -t ./ .  --load-list /tmp/tmpe9wwqlo3
running=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_TEST_TIMEOUT=60 ${PYTHON:-python} -m subunit.run discover -t ./ .  --load-list /tmp/tmpbwa1_yy8
running=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_TEST_TIMEOUT=60 ${PYTHON:-python} -m subunit.run discover -t ./ .  --load-list /tmp/tmpqv7w0s4v
running=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_TEST_TIMEOUT=60 ${PYTHON:-python} -m subunit.run discover -t ./ .  --load-list /tmp/tmpqdbg34vv
running=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_TEST_TIMEOUT=60 ${PYTHON:-python} -m subunit.run discover -t ./ .  --load-list /tmp/tmpkd169eki
running=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_TEST_TIMEOUT=60 ${PYTHON:-python} -m subunit.run discover -t ./ .  --load-list /tmp/tmpch2tkra0
running=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_TEST_TIMEOUT=60 ${PYTHON:-python} -m subunit.run discover -t ./ .  --load-list /tmp/tmp6h4ef3mr
running=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_TEST_TIMEOUT=60 ${PYTHON:-python} -m subunit.run discover -t ./ .  --load-list /tmp/tmp6407izna
Ran 57 tests in 2.529s (-0.726s)
PASSED (id=9, skips=8)

(pbr3)$ python setup.py testr --testr-args --concurrency=1
running testr
running=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_TEST_TIMEOUT=60 ${PYTHON:-python} -m subunit.run discover -t ./ .  
Ran 57 tests in 9.140s (-0.065s)
PASSED (id=7, skips=8)

ページ21: Pythonic なコード

エキスパート Python プログラミングから引用しました。

Pythonic とは、小さなコードパターンに最も効率的なイディオムを使ったコーディングや構文

最近、他人の Python コードを読んでいて「何かおかしい」と違和感を感じるときは、この Pythonic の概念を大事にしてコードを書いているかどうかの違いによるもののような気がしてきました。OSSフレームワークやライブラリを使うときにも、その開発者の意図に共感できると親しみがもてたりするものです。

Pythonista というと、一貫性にこだわる怖い人たちなんでしょう?のイメージがあります。もちろんそういった場面にもよく遭遇しますが、

Although practicality beats purity. ( とはいえ、純粋さよりも実用的であること )

  • PEP8 の一節

A Foolish Consistency is the Hobgoblin of Little Minds

とか言っていて、一貫性にこだわり過ぎるのもよくないという戒めもあります。こだわりは強くても極論はよくないといった感じですかね。

ページ23: functools.singledispatch

汎用関数が気持ちよく書ける、デコレーターの応用例の紹介をしました。
勉強会でも singledispatch をコードリーディングしたのですが、ソースもシンプルなのでデコレーターの実用例としておもしろいと思います。

ページ33: Python 2.4 との互換性を気にする

これを気にしなくなって Python3 に感激したのも事実の1つです。

標準モジュールがなかったり、機能のバージョン毎の差異を吸収するように実装したものがいくつかありました (ここにサンプルコードを置きました) 。2.4 対応を考慮しながらコードを書くと、常にこういったことに悩まされながらコードを書いているという例です。あくまで自分の用途にあわせた実装なのでサンプルコードは参考程度にみてください。

  • json の読み込み
  • itemgetter の複数引数の対応
  • datetime.strptime の互換関数

ページ34: 集合型 set の応用例

これもスライドを飛ばしたけど、集合型をこんな用途にも使えるんだなぁと感心したスニペットです。アルゴリズムを勉強したくなる気を起こさせるコードでした。

def has_invalid_fields_loop(fields):                                             
    """                                                                          
    >>> has_invalid_fields_loop(['foo', 'bar', 'foo', 'test'])                      
    True                                                                            
    >>> has_invalid_fields_loop(['foo', 'bar'])                                     
    False                                                                           
    """                                                                             
    for field in fields:                                                            
        if field not in ['foo', 'bar']:                                             
            return True                                                             
    return False                                                                    
                                                                                    
                                                                                    
def has_invalid_fields_set(fields):                                                 
    """                                                                             
    >>> has_invalid_fields_set(['foo', 'bar', 'foo', 'test'])                       
    True                                                                            
    >>> has_invalid_fields_set(['foo', 'bar'])                                      
    False                                                                           
    """                                                                             
    return bool(set(fields) - set(['foo', 'bar']))

ページ35: super はスーパーな話

簡単な分かりやすいサンプルコードを提示したつもりだったのに、絶賛大ブーイングを受けたサンプルコードがこれでした。こんな複雑なことをする意図が分からない的な。

メソッド解決順序(MRO) がとても分かりやすいけれど、全てのクラスが object を継承するようになったため、どんな多重継承を行っても必ずダイヤモンド継承になる。多重継承が本当に必要かどうかの議論はもちろんあって良いですが、mixin 的に機能を扱いたいときとか、普通にその MRO や移譲先を意識して設計するでしょうし、サブクラスで拡張することを意識してコア機能作ったりするんじゃないかなぁとも思います。フレームワークプラグインのような、それぞれの開発者が異なるケースでもたくさん応用例がありそうに思いますが、ちゃんと調べないと分からないぐらいにはややこしいです。

後半で時間がなかったからちゃんと背景を説明をしなかったせいでもあるけれど、もっと私も勉強しないといけないです。

ページ42: The Hacker's Guide to Python

Python WeeklyThe Hacker's Guide to Python が紹介されていて、機会があったら読もうと思っていました。ちょうど勉強会するからと読み始めたらおもしろくて、この本を読んだために勉強会の内容もかなり変わってしまったぐらい影響を受けました。本当は AST やパフォーマンスの章も紹介したかったけれど、準備が全然間に合わなくて断念しました。

まとめ

当初は社内の Python ツールリファクタリング的な話をしながら Python3 楽しいよという話をしようと構想していました。いろいろ調べたり、The Hacker's Guide to Python を読んだりしているうちにどんどん変わっちゃってこんな感じになってしまいました。せっかく資料をまとめたので何かの役に立ったら嬉しいです。