前回は基調講演とカンファレンスの全体像について紹介しました。今回と次回ではPythonそのものの講演について紹介したいと思います。
今回はCPythonというC言語で実装されたPythonの講演
CPython
CPythonの講演で紹介するのは、

- ※ 特に注記がない場合、
サンプルコードはPython 2. 7.2で実行しています。
Advanced Python: 高度なPythonテクニック
「Advanced Python」
なお、

最適化
3つの最適化のテクニックについて紹介されていました。
- グローバルな参照
(組み込み関数、 モジュール、 グローバル変数)をローカルの参照に置き換える - 束縛メソッドを使う
- 内部ループにおけるピュアPythonの関数呼び出しを最小化する
まずは、
>>> from dis import dis
>>> class Foo(object):
... def bar(self):
... pass
>>> f = Foo()
>>> dis(lambda: f.bar())
1 0 LOAD_GLOBAL 0 (f)
3 LOAD_ATTR 1 (bar)
6 CALL_FUNCTION 0
9 RETURN_VALUE
>>> dis(lambda given_f_bar: given_f_bar())
1 0 LOAD_FAST 0 (given_f_bar)
3 CALL_FUNCTION 0
6 RETURN_VALUE
無名関数でf.LOAD_
とLOAD_
の命令がなくなります。
Pythonは関数
3つ目の、
例えば、pow()
と、
$ python -m timeit "map(lambda x, y: x ** y, [1,2,3], [1,2,3])"
1000000 loops, best of 3: 1.85 usec per loop
$ python -m timeit "map(pow, [1,2,3], [1,2,3])"
1000000 loops, best of 3: 1.38 usec per loop
さらにベクトル化により、
>>> [ord(i) for i in string.letters]
>>> # こういった処理は map() の方が速い?
>>> map(ord, string.letters)
筆者の環境
時間計測
timeitモジュールを利用するサンプルの1つとして、
def use_listcomp(data):
return [i ** 2 for i in data]
def use_itertools(data):
from itertools import imap, repeat
return list(imap(pow, data, repeat(2)))
if __name__=='__main__':
from timeit import Timer
from random import random
n = 10000
data = [random() for i in range(n)]
setup = "from __main__ import use_listcomp, use_itertools, data"
for func in use_listcomp, use_itertools:
stmt = '{0.__name__}(data)'.format(func)
print func.__name__, min(Timer(stmt, setup).repeat(7, 20))
このプログラムを実行すると、itertools
モジュールのツールを使った処理の時間を計測して、
$ python get_timing.py
('use_listcomp', 0.022819042205810547)
('use_itertools', 0.028668880462646484)
筆者は、
ディクショナリで考える
Pythonは、
>>> x = 1
>>> x == globals()['x']
True
>>> globals()['x'] = 2
>>> x
2
ハンズオンでは、
class Animal(object):
def __init__(self, name):
self.name = name
def walk(self):
print '{} is Walking'.format(self.name)
class Dog(Animal):
def bark(self):
print 'Woof'
if __name__ == '__main__':
d = Dog('Shiro')
print d.name
d.bark()
d.walk()
実際、
>>> Animal.__dict__.keys()
>>> ['__module__', 'walk', '__dict__', '__weakref__', '__doc__', '__init__']
>>> d.__class__.__dict__.keys()
>>> ['__module__', 'bark', '__doc__']
>>> d.__dict__
>>> {'name': 'shiro'}
Pythonにおけるクラスの仕組みを理解するために、
from pprint import pprint
def init_function(self, name):
self['name'] = name
def walk_function(self):
name = get_attribute(self, 'name')
print '{} is Walking'.format(name)
def bark_function(self):
print 'Woof'
Animal = dict(
__name__ = 'Animal',
__bases__ = (),
__doc__ = 'Generic animal class',
__init__ = init_function,
walk = walk_function,
)
Dog = dict(
__name__ = 'Dog',
__bases__ = (Animal,),
__doc__ = "Man's best frient",
bark = bark_function,
)
def class_lookup(klass, key):
if key in klass:
return klass[key]
for klass in klass['__bases__']:
result = class_lookup(klass, key)
if result is not None:
return result
return None
def make_instance(klass, *args):
instance = dict(__class__=klass)
init = class_lookup(klass, '__init__')
if init is not None:
init(instance, *args)
return instance
if __name__ == '__main__':
d = make_instance(Dog, 'Shiro')
pprint(d)
このプログラムの実行結果です。Dogディクショナリが次のように表現されます。__
にDogディクショナリ、__
$ python think_dict.py
{'__class__': {'__bases__': ({'__bases__': (),
'__doc__': 'Generic animal class',
'__init__': <function init_function at 0x1004979b0>,
'__name__': 'Animal',
'walk': <function walk_function at 0x1004977d0>},),
'__doc__': "Man's best frient",
'__name__': 'Dog',
'bark': <function bark_function at 0x1004a12a8>},
'name': 'Shiro'}
ここからは自分で挑戦する3つの練習問題が与えられました。もっと適切な実装があるかもしれませんが、
# Exercise 1: implement get_attribute function
def get_attribute(instance, attrname):
if attrname in instance:
return instance[attrname]
return class_lookup(instance['__class__'], attrname)
get_attribute(d, 'bark')(d)
get_attribute(d, 'walk')(d)
メソッドを取得して呼び出した実行結果です。
Woof
Shiro is Walking
次の練習問題は、
# Exercise 2: implement super function
def super_function(instance, attrname):
for klass in instance['__class__']['__bases__']:
_super = class_lookup(klass, attrname)
if _super is not None:
return _super
raise AttributeError
def override_walk_function(self):
super_function(self, 'walk')(self)
print 'overridden!'
d['walk'] = override_walk_function
get_attribute(d, 'walk')(d)
Dogディクショナリのオブジェクトwalk
をoverride_
としてオーバーライドして、
Shiro is Walking
overridden!
最後の練習問題は、
# Exercise 3: implement simple help()
def help_function(obj):
_obj = obj['__class__'] if obj.get('__class__') else obj
klasses = (_obj,) + _obj['__bases__']
help_info = {}
for klass in klasses:
klass_name = klass['__name__']
help_info[klass_name] = dict(method={}, attribute={})
for key, value in klass.iteritems():
if key != '__bases__':
_attr = 'method' if callable(value) else 'attribute'
help_info[klass_name][_attr][key] = value
pprint(help_info)
help_function(d) # same as help_function(Dog)
表示の整形を行っていませんが、
{'Animal': {'attribute': {'__doc__': 'Generic animal class',
'__name__': 'Animal'},
'method': {'__init__': <function init_function at 0x1004979b0>,
'walk': <function walk_function at 0x1004977d0>}},
'Dog': {'attribute': {'__doc__': "Man's best frient", '__name__': 'Dog'},
'method': {'bark': <function bark_function at 0x1004a12a8>}}}
Pythonの名前空間はディクショナリで実現されていて、
Fun With Python’s Newer Tools: Pythonの新しいツールで遊ぼう
「Fun With Python’s Newer Tools」
なお、

collections.Counter
collections.
>>> from collections import Counter
>>> c = Counter()
>>> c["a"] += 1
>>> c["b"] += 1
>>> c["a"] += 1
>>> c
Counter({'a': 2, 'b': 1})
collections.Counterの実装
Counter
は、Counter
のソースを読んでみると、
- __
missing__ 先に紹介したCounterのサンプルコードで
c[“a”] += 1
でKeyError
が発生しないことに気付いた方もいると思います。__missing__ という特殊メソッドを定義することで、ディクショナリのキーが存在しないときの処理をフックできます。 Counter
では、次のようにゼロを返す実装になっています。 def __
missing__ (self, key): 'The count of elements not in the Counter is zero.' # Needed so that self[missing_item] does not raise KeyError return 0- __
delitem__ Counter
に存在しないキーを削除しようとしたときは、KeyError
を発生させないようになっています。キーが存在するときはdict
にその処理を委譲しています。def __
delitem__ (self, elem): 'Like dict.__delitem__() but does not raise KeyError for missing values.' if elem in self: dict.__delitem__ (self, elem)
collections.namedtuple
collections.
namedtupleのシンプルな実装例は次になります。____
を作成せず、
from operator import itemgetter
class TestResults(tuple):
__slots__ = ()
_field = ('failed', 'attempted')
def __new__(_cls, failed, attempted):
'Create new instance fo TestResults(failed, attempted)'
return tuple.__new__(_cls, (failed, attempted))
def __repr__(self):
'Return nicely formatted representation string'
return self.__class__.__name__ + '(failed=%r, attempted=%r)' % self
failed = property(itemgetter(0), doc='Alias for field number 0')
attempted = property(itemgetter(1), doc='Alias for field number 1')
collections.namedtupleの応用例
- フィールド構造を拡張する
先ほど紹介したTestResultsを_fieldsを使って拡張します。
>>> LabeledResults = namedtuple('LabeledResutls', TestResults._fields + ('test_
name' ,) )- インスタンスのプロトタイプとして使う
プロトタイプとして定義し_replace()メソッドで要素を変更できます。
>>> cell = namedtuple('Cell', 'color size border') >>> prototype = cell(color='red', size=10, border=False) >>> prototype Cell(color='red', size=10, border=False) >>> intro = prototype._replace(size=20) >>> intro Cell(color='red', size=20, border=False)
- サブクラス化する
-
>>> class Point(namedtuple('Point', 'x y')): ... __
slots__ = () ... @property ... def hypot(self): ... return (self.x ** 2 + self.y ** 2) ** 0.5 ... def __str__ (self): ... return 'Point: x=%6.3f y=%6. % ( ... self.x, self.y, self.hypot)3f hypot=%6. 3f' - 列挙型(enum)を作る
-
>>> color = namedtuple('Color', ... 'red orange yellow')._make(range(3)) >>> color.red 0 >>> color.yellow 2
これらのサンプルは標準ライブラリドキュメントでも紹介されています。
LRUキャッシュ
functools.
>>> from functools import lru_cache
>>> @lru_cache(maxsize=None)
... def fib(n):
... if n < 2:
... return n
... return fib(n-1) + fib(n-2)
...
>>> print([fib(n) for n in range(16)])
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]
>>> print(fib.cache_info())
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)
>>> fib.cache_clear()
>>> fib.cache_info()
CacheInfo(hits=0, misses=0, maxsize=None, currsize=0)
※ このサンプルコードはPython 3.
このサンプルも標準ライブラリドキュメントから引用しています。cache_
やcache_
がデコレートされる関数の属性に追加されます。
Python Tips、Tricks、And Idioms:Pythonのコツ、トリック、イディオム
「Python Tips、
- Python固有の機能とイディオム
- 比較とソート
- イテレーターを使ったプログラミング
- 動的なプログラミング
- コレクション型
- superができること
- 最適化とハック
- デバッガ
- 開発ツールとその環境
本講演はPYTHON TIPS、

比較とソート
次のようなソート関数
- sorted()
- list.
sort() - min()
- max()
- heapq.
nsmallest() - heapq.
nlargest() - bisect.
bisect() - bisect.
insrot()
これらはx < y
の比較をするときに__
>>> from operator import lt
>>> class MyInt(int):
... def __init__(self, num):
... self.num = num
... def __lt__(self, x):
... return not lt(self.num, x)
...
>>> sorted(map(MyInt, [1,5,3,2,4]))
[5, 4, 3, 2, 1]
またbisectモジュール以外のソート関数key
引数でソートに使う値を指定することで、
>>> sorted("cbAdaB")
['A', 'B', 'a', 'b', 'c', 'd']
>>> # 大文字小文字を無視する
>>> sorted("cbAdaB", key=str.lower)
['A', 'a', 'b', 'B', 'c', 'd']
operatorモジュールと組み合わせても便利です。リスト内のタプルの2番目の要素でソートします。
>>> from operator import itemgetter
>>> sorted([(3, 3), (1, 2), (2, 1)], key=itemgetter(1))
[(2, 1), (1, 2), (3, 3)]
オブジェクトの属性でソートします。
>>> from operator import attrgetter
>>> from collections import namedtuple
>>> user = namedtuple('User', 'name age')
>>> sorted([user('t2y', 32), user('rokujyouhitoma', 27)],
... key=attrgetter('age'))
[User(name='rokujyouhitoma', age=27), User(name='t2y', age=32)]
引数でソート順序を逆にできます。
>>> sorted([1, 2, 3], reverse=True)
[3, 2, 1]
ソートのサンプルについてはSorting HOW TOも参考にしてください。
イテレーターを使ったプログラミング
Pythonという言語がシンプル且つ簡潔たらしめる重要な概念がイテレーターであると説明されていました。__
をサポートするシーケンスや__
をサポートする任意のオブジェクトが、
ファイルを10バイトずつ読み込むサンプルコードが紹介されていました。普通に実装すると次のようになります。
>>> with open(file_name) as f:
... while True:
... block = f.read(10)
... if block = '':
... break
この処理をイテレーターを使って実装すると次のようになります。
>>> from functools import partial
>>> with open(file_name) as f:
... for block in iter(partial(f.read, 10), ''):
... do_something
functools.iter
の第2引数
後者の実装は、block
の処理のみをループ内に記述できます。筆者はpartial
やiter
を使ったことがなく、
コレクション型
コレクション型を扱うcollectionsモジュールは、
- デック
(deque) デック
(deque) は2. 4で追加されました。両端から要素を追加・ 取り出せるキューです。 >>> from collections import deque >>> d = deque() >>> d.append(1) # 右端へ追加 >>> d.append(2) >>> d deque([1, 2]) >>> d.popleft() # 左端から取り出す 1
- 名前付きタプル
(namedtuple) 名前付きタプル
(namedtuple) は2. 5で追加されました。フィールド名でもアクセスできるタプルです。 >>> from collections import namedtuple >>> Point = namedtuple('Point', 'x y') >>> p = Point(1, 2) >>> p.x, p.y # フィールド名で値を取得 (1, 2)
- デフォルトディクショナリ
(defaultdict) デフォルトディクショナリ
(defaultdict) は2. 6で追加されました。ファクトリ関数でデフォルト値を生成するディクショナリです。 >>> from collections import defaultdict >>> d = defaultdict(list) >>> d['a'].append(1) # 空リストが生成されて値が追加される >>> d defaultdict(<type 'list'>, {'a': [1]})
- カウンタ
(Counter) カウンタ
(Counter) は2. 7で追加されました。数の集計を意図したディクショナリです。 >>> from collections import Counter >>> c = Counter() >>> c['a'] += 1 >>> c Counter({'a': 1})
- 順序付きディクショナリ
(OrderedDict) 順序付きディクショナリ
(OrderedDict) は2. 7で追加されました。要素を追加した順序を維持するディクショナリです。 >>> from collections import OrderedDict >>> d = {'apple': 1, 'macbook': 2, 'iphone': 3} >>> d {'iphone': 3, 'apple': 1, 'macbook': 2} >>> OrderedDict(sorted(d.items(), key=lambda (k, v): k)) OrderedDict([('apple', 1), ('iphone', 3), ('macbook', 2)])
手元のコードを見返してみると、
ハック
ある程度大きな既存のディクショナリのキーや値の探索を高速化するハックだそうです。
>>> d = {'key': '既存の大きなディクショナリ', ...}
>>> d.update(dict(d))
dict.
メソッドは、
The Art Of Subclassing:アート・オブ・サブクラス
「The Art Of Subclassing」
- クラス、
サブクラス、 インスタンスの概念を再確認する - ユースケース、
原則、 デザインパターンを議論する - super()の動作とその使い方を確認する
- Bloom Filterの事例研究
なお、
サブクラス化のパターン
フレームワークで使われるパターンとしてSimpleHTTPServerとcmdモジュールを紹介されていました。
SimpleHTTPServerは、do_
やdo_
といったスタブメソッドをもつリクエストハンドラを提供します。親クラスのBaseHTTPServer
やSocketServer
からリクエストハンドラの他の機能を継承することで、GET
やHEAD
に対する処理をサブクラスで簡単に拡張できます。
リクエストに対して、cmd
モジュールの方が分かりやすいのでそちらで説明します。
行指向のコマンドインタープリターを書くフレームワークを提供するCmd
クラスのonecmdメソッドではgetattr
を使って、self
def onecmd(self, line):
...
try:
func = getattr(self, 'do_' + cmd)
except AttributeError:
return self.default(line)
return func(arg)
次のように子クラスでdo_
に続くメソッドを実装することで、getattr
で任意のメソッドが取得できます。
>>> from cmd import Cmd
>>> class MyCmd(Cmd):
... def do_print(self, args):
... print "do_print", args
>>>
>>> c = MyCmd()
>>> c.onecmd("print hello")
do_print hello
SimpleHTTPServerのリクエストハンドラによるdo_
やdo_
の処理も本質的にはこれと同じです。
OCP(open/closed principle)とネームマングリング
クラスのメソッド名の先頭に__
class MyDict(dict):
def __init__(self, iterable):
self.items_list = []
self.__update(iterable)
def update(self, iterable):
for item in iterable:
self.item_list.append(item)
__update = update
このクラスをインスタンス化すると、update
メソッドのエイリアスが
__
ではなくd._MyDict__
として作成されます。
>>> d = MyDict([])
>>> d._MyDict__update
<bound method MyDict.update of {}>
ここで__
を定義している意図は、update
がオーバーライドされても、__
の処理が変更されないようにするためです。
class MySubDict(MyDict):
def update(self, iterable):
pass
>>> d = MyDict([1,2,3])
>>> d.items_list
[1, 2, 3]
>>> d2 = MySubDict([1,2,3])
>>> d2.items_list
[1, 2, 3]
オブジェクト指向プログラミングにおけるクラスの、
- 拡張に対して開いて
(open) いなければならない - 修正に対して閉じて
(closed) いなければならない
という設計上の原則をOCP(open/
このテクニックはMyDict
の内部的な呼び出しをサブクラスの拡張から保護するための方法で、
- Tip:
- Raymond氏は__
(2重アンダースコア) を 「あんだーあんだー」 と読んでいました。また、 カリフォルニアの人たちには 「だんだー」 と読む人もいるそうです。
Pythonのsuper()ができること
super
は新スタイルクラスsuper
を使って委譲することで、
>>> from collections import Counter, OrderedDict
>>> class OrderedCounter(Counter, OrderedDict):
... pass
>>> OrderedCounter('abracadabra')
OrderedCounter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})

このサンプルは、Counter
とOrderedDict
を継承してOrderedCounter
を定義します。Counter
はdict
のサブクラスで、__
とupdate()
メソッドの中でsuper
を使ってその処理を委譲します。Counter
とOrderedDict
の実装の一部が次になります。
class Counter(dict):
def __init__(self, iterable=None, **kwds):
super(Counter, self).__init__()
self.update(iterable, **kwds)
def update(self, iterable=None, **kwds):
...
super(Counter, self).update(iterable)
...
class OrderedDict(dict):
def __init__(self, *args, **kwds):
...
self.__update(*args, **kwds)
update = MutableMapping.update
__update = update # let subclasses override update without breaking __init__
新スタイルクラスのメソッド解決順序
>>> OrderedCounter.__mro__
(<class '__main__.OrderedCounter'>,
<class 'collections.Counter'>,
<class 'collections.OrderedDict'>,
<type 'dict'>,
<type 'object'>)
メソッドを調べる順番がOrderedCounter
-> Counter
-> OrderedDict
の順番であるのが分かります。
実際の呼び出しは次のようになります。Counter.__
内にsuper
の呼び出しがあるのでOrdereedDict.__
が呼ばれます。その次にself.
(Counter.
)メソッド内でもsuper
の呼び出しがあるのでOrdereedDict.
が呼ばれます。
説明が前後してしまいますが、Counter
のメソッド解決順序Counter
-> dict
であることが分かります。
>>> Counter.__mro__
(<class 'collections.Counter'>,
<type 'dict'>,
<type 'object'>)
つまりCounter
の__
メソッドとupdate()
メソッド内のsuper
の移譲先が、OrderedCounter
を定義したことにより、dict
からOrderedCounter
に変更されたことが分かります。
- Tip
- Python3では
superを引数なしで呼び出せます。その際は、 第1引数に現在のクラスが、 第2引数に現在のメソッドの第1引数が使われます。例えば、 この Counter
のサンプルの場合、super
と(Counter、 self) .__ init__() super().__
は同じです。init__()
詳細に興味がある方はPython’s super() considered super!とそこで紹介されているサンプルコードHow to use super() effectively – Python 2.
次回