2014年12月26日金曜日

Pythonのデコレータを理解するための12Step

http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/

Step1. 関数

>>> def foo():
... return 1
...
>>> foo()
1
これは基本ですね. Pythonにおいて関数はdefキーワードにより関数名とパラメータのリスト(任意)とともに定義できます.また括弧付きで名前を指定することで実行できます.

Step2. スコープ

Pythonでは関数を作ると新しいスコープが作られます.言い方を変えるとそれぞれの関数がそれぞれに名前空間を持つということです.

Pythonではこれらを確認することのできる組み込み関数も用意されていて, locals()で自身の持つローカルな名前空間の値を辞書形式で返却します.

>>> def foo(arg):
... x = 10
... print locals()
...
>>> foo(20)
{'x': 10, 'arg': 20}
またグローバルな名前空間についてはglobals()で同様に確認ができます.

>>> y = 30
>>> globals()
{..., 'y': 30} #Pythonが自動的に作るグローバル変数が他にも表示されるが省略
Step3. 変数の解決規則

Pythonにおける変数の解決ルールは

作成の際には常に新しい変数がその名前空間の中に作られる
参照は同じ名前空間内を検索し, 無ければ外側に検索を広げていく
というものです.関数のローカルスコープでグローバルスコープの変数を参照してみましょう.

>>> text = "I am global!"
>>> def foo():
... print text #1
...

>>> foo()
I am global!
関数の外側で定義したtextの内容がきちんと表示されていますね.#1ではまず関数内のローカル変数を探し, 無いので同じtextという名前のグローバル変数を探しにいっています.

では今度は, 関数の外側で定義した変数を, 関数内で変更してみましょう.

>>> text = "I am global!"
>>> def foo():
... text = "I am local!" #2
... print locals()
...
>>> foo()
{'text': 'I am local!'}
>>> text
'I am global!'
foo()が呼ばれた時にはtextの内容として関数内で代入した値がセットされていますが, 外側のグローバル変数のtextの値は変わっていません. #2では実際のところグローバル変数を探しに行かず, 関数foo内に新しいローカル変数textが作られているのです.

つまり関数の内側ではグローバル変数は参照はできるものの代入はできない, ということになります。

Step4. 変数のライフタイム

スコープだけでなくライフタイムについても知っておく必要があります.

>>> def foo():
... x = 1
...
>>> foo()
>>> print x #1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
ここでエラーが起こっているのは, 上述のスコープの問題だけではありません. 名前空間は関数fooが呼ばれる都度作られ, 処理が終わると無くなってしまいます.つまり上の例では#1のタイミングでは文字通りxという変数はどこにも存在していない, ということです.

Step5. 関数の引数とパラメータ

Pythonでは関数に引数を与えることができます. 定義時のパラメータの名前が, ローカル変数の名前として使われます.

>>> def foo(x):
... print locals()
>>> foo(1)
{'x': 1} # パラメータxがローカル変数名として使われている
Pythonでは関数にパラメータを設定したり, 呼び出し時に引数を与える様々な方法が提供されています. パラメータには必須パラメータと任意となるデフォルトパラメータがあります.

>>> def foo(x, y=0): # 1
... return x - y
>>> foo(3, 1) # 2
2
>>> foo(3) # 3
3
>>> foo() # 4
Traceback (most recent call last):
...
TypeError: foo() takes at least 1 argument (0 given)
>>> foo(y=1, x=3) # 5
2
#1の例ではxがパラメータ, デフォルトの値を指定しているyがデフォルトパラメータになります.#2が通常の関数使用例ですが, #3のようにデフォルトパラメータについては省略することも可能です. 省略された引数はデフォルトの値(この場合0)を取ります.

またPythonについては名前付き引数も使えるので, 名前を指定することで順番を気にせず引数を指定することもできます(#4). Pythonにおける引数はただの辞書だと理解していれば納得のいく動きです.

Step6. 関数のネスト

Pythonでは関数内にネストして関数を定義できます.

>>> def outer():
... x = 1
... def inner():
... print x # 1
... inner() # 2
...
>>> outer()
1
ルールはこれまで見てきたものと同じです. つまり#1ではローカル変数xを探すが見つからないので外側の名前空間を探し, outer内で定義されあているxを参照します.また#2ではinner()を呼び出していますが, ここで大切なのは innerというのも変数名の1つにすぎず, 解決規則に基づきouter内の名前空間内の定義を探して呼び出されている ということです.

Step7. 関数はPythonにおいてファーストクラスオブジェクトである

Pythonでは関数自身もまたオブジェクトにすぎません.

>>> issubclass(int, object) # Python内の全てのオブジェクトは同じ共通のクラスを継承して作られている
True
>>> def foo():
... pass
>>> foo.__class__
<type 'function'>
>>> issubclass(foo.__class__, object)
True
これが何を意味するかというと, 関数を一般的な他の変数と何らかわりなく扱える - つまり他の関数の引数として与えたり, 関数の戻り値として使うことができるということです.

>>> def add(x, y):
... return x + y
>>> def sub(x, y):
... return x - y
>>> def apply(func, x, y):
... return func(x, y)
>>> apply(add, 2, 1)
3
>>> apply(sub, 2, 1)
1
上の例では関数applyはパラメータとして指定された関数の実行結果を戻り値とするようになっていて, 関数add, subが引数として(他の変数と何ら変わりなく)与えられているのがわかります.

次の例はどうでしょうか.

>>> def outer():
... def inner():
... print "Inside inner"
... return inner # 1
...
>>> foo = outer() #2
>>> foo
<function inner at 0x...>
>>> foo()
Inside inner
上のapplyの例と異なるのは, #1で戻り値として実行結果ではなく関数そのものを指定しているということです.(inner()ではなくinnerを与えている)

これは#2のように普通に代入可能で, fooに関数が入っていて実行することもできることがわかります.

Step.8 クロージャ

上の例を少しだけ変えて見てみましょう.

>>> def outer():
... x = 1
... def inner():
... print x
... return inner
>>> foo = outer()
>>> foo.func_closure
(<cell at 0x...: int object at 0x...>,)
innerはouterによって返される関数で, fooに格納され, foo()により実行可能です...本当に?

Pythonの変数解決規則には完璧に従っていますが, ライフサイクルはどうなっていますか?変数xはouter関数が実行されている間のみ存在しています. ここではouter関数の処理が終わった後にinner関数がfooに代入されているので, foo()は実行できないのではないですか?

…この予想に反して, foo()は実行可能です。PythonがFunction clojures(クロージャ)の機能を持っているからで, これは グローバルスコープ以外で定義された関数(この場合inner)が, 「定義時」の自分を囲むスコープの情報を記憶している というものです. 実際に記憶されている事が上のようにfunc_closureプロパティを呼び出すことで確認できます.

「定義時」と書いたことを思い出してください. inner関数はouter関数が呼び出されるたびに新しく定義されています.

>>> def outer(x):
... def inner():
... print x
... return inner
>>> print1 = outer(1)
>>> print2 = outer(2)
>>> print1()
1
>>> print2()
2
上の例ではprint1やprint2に直接値を引数として入れなくても, それぞれの内部のinner関数が何の値を出力すべきかを記憶しているのです. これを利用して固定の引数を取るようカスタマイズした関数を生成することもできるということですね.

Step.9 デコレータ

いよいよデコレータの話に入ります.
ここまでの話を踏まえて先に結論から言うと, デコレータとは「関数を引数に取り, 引き換えに新たな関数を返すcallable(*)」です.

>>> def outer(some_func):
... def inner():
... print "before some_func"
... ret = some_func() #1
... return ret + 1
... return inner
>>> def foo():
... return 1
>>> decorated = outer(foo) #2
>>> decorated()
before some_func
2
1つ1つ理解していきましょう.
ここでパラメータとしてsome_funcを取るouterという関数を定義しています. そしてouterの中でさらにinnerという内部関数を定義しています.
innerは文字列をprintした後, #1で返却する値を取得しています. some_funcはouterが呼び出されるごとに異なる値を取りえますが, ここではそれが何であれとりあえず実行(call)し, その実行結果に1を加えた値を返却します.
最後にouter関数はinner関数そのものを返却します.

#2ではsome_funcとしてfooを引数にouterを実行した戻り値を変数decoratedに格納しています. fooを実行すると1が返ってきますが, outerを被せたdecoratedではそれに1プラスされて2が返ってきます. 言ってみればdecoratedは, fooのデコレーション版(foo + 何かの処理)といえます.

実際に有用なデコレータを使う際には, decoratedのように別の変数を用意せずfooそのものを置き換えてしまうことが多いです. つまり下のように.

>>> foo = outer(foo)
もう元のfooは呼ばれず, 常にデコレーションされたfooが戻ることになりますね. もう少し実用的な例も見てみましょう.

とある座標のオブジェクトを保持するライブラリを使っているとします. そのオブジェクトはxとyの座標ペアを保持していますが, 残念ながら足し算や引き算など数字の処理機能を持っていません. しかし我々はこのライブラリを用いて大量の計算処理をする必要があり, ライブラリのソースを改編することも好ましくない状況だとします. どうすれば良いでしょうか?

アプローチとしては下のようにadd, subのような関数を作ってやればよいでしょう.

>>> class Coordinate(object):
... def __init__(self, x, y):
... self.x = x
... self.y = y
... def __repr__(self):
... return "Coord: " + str(self.__dict__)
>>> def add(a, b):
... return Coordinate(a.x + b.x, a.y + b.y)
>>> def sub(a, b):
... return Coordinate(a.x - b.x, a.y - b.y)
>>> one = Coordinate(100, 200)
>>> two = Coordinate(300, 200)
>>> add(one, two)
Coord: {'y': 400, 'x': 400}
ここでたとえば「扱う座標系は0が下限である必要がある」といったチェック処理が必要だとしたらどうしますか?
つまり, 現状では

>>> one = Coordinate(100, 200)
>>> two = Coordinate(300, 200)
>>> three = Coordinate(-100, -100)
>>> sub(one, two)
Coord: {'y': 0, 'x': -200}
>>> add(one, three)
Coord: {'y': 100, 'x': 0}
のようになりますが, sub(one, two)は(0, 0)を, add(one, three)は(100, 200)を返して欲しいということです. それぞれの関数に下限のチェックを入れていくということも考えられますが, ここでデコレータを使ってチェック処理を一元化してみましょう!

>>> def wrapper(func):
... def checker(a, b):
... if a.x < 0 or a.y < 0:
... a = Coordinate(a.x if a.x > 0 else 0, a.y if a.y > 0 else 0)
... if b.x < 0 or b.y < 0:
... b = Coordinate(b.x if b.x > 0 else 0, b.y if b.y > 0 else 0)
... ret = func(a, b)
... if ret.x < 0 or ret.y < 0:
... ret = Coordinate(ret.x if ret.x > 0 else 0, ret.y if ret.y > 0 else 0)
... return ret
... return checker
>>> add = wrapper(add)
>>> sub = wrapper(sub)
>>> sub(one, two)
Coord: {'y': 0, 'x': 0}
>>> add(one, three)
Coord: {'y': 200, 'x': 100}
前の foo = outer(foo) とやっていることは変わりませんが, 有用なチェック機構をパラメータと関数の処理結果に対して適用することができています.

Step.10 @シンボルの適用

Pythonでは, デコレータの記述に関して@記号を用いたシンタックスシュガーが用意されています. すなわち

>>> add = wrapper(add)
という記述は,

>>> @wrapper
... def add(a, b):
... return Coordinate(a.x + b.x, a.y + b.y)
という形で記述できます.

どうでしょう. ここまで読んで, classmethod や staticmethod のように有用なデコレータを自分で作ることはなかなかレベルが高い話ですが, 少なくともデコレータを使うのはそんなに難しくない - ただ @decoratorname を前置してやるだけだ! - と思っていただけると幸いです.

Step.11 *args と **kwargs

上で書いたデコレータwrapperは有用ですが, パラメータが2つという限られた関数にしか適用できません. 今回はそれでも良いのですが, もっとあらゆる関数に適用できるようなデコレータを書きたいと思ったらどうすれば良いでしょうか?
Pythonにはこれをサポートする機能も用意されています. 詳細は公式のドキュメントを読むのが良いですが, 関数を定義する際にパラメータに*(アスタリスク)を付けてやると任意の数の必須パラメータを受け付けることができます.

>>> def one(*args):
... print args
>>> one()
()
>>> one(1, 2, 3)
(1, 2, 3)
>>> def two(x, y, *args):
... print x, y, args
>>> two('a', 'b', 'c')
a b ('c',)
任意のパラメータ部分に関してはリストで渡されているのがわかりますね. また定義時ではなく呼び出し時に引数に*を付けてやると, 既にリストやタプルになった引数をアンパックして固定引数に適用してくれます.

>>> def add(x, y):
... return x + y
>>> lst = [1,2]
>>> add(lst[0], lst[1]) #1
3
>>> add(*lst) #2 <- #1と全く同じ意味を指します
3
また**(アスタリスク2個)という記法もあります. こちらはリストではなく辞書形式に対応しています.

>>> def foo(**kwargs):
... print kwargs
>>> foo()
{}
>>> foo(x=1, y=2)
{'y': 2, 'x': 1}
**kwargsを関数定義に使うことは「全ての明示的に指定していないパラメータはkwargsという名前の辞書に格納される」ことを意味します. *argsと同じく関数呼び出し時のアンパックにも対応しています.

>>> dct = {'x': 1, 'y': 2}
>>> def bar(x, y):
... return x + y
>>> bar(**dct)
3
Step.12 ジェネリックなデコレータ

上の機能を用いて, 関数の引数をログに出力するデコレータを書いてみましょう. 簡略化のためログ出力はstdoutにprintしています.

>>> def logger(func):
... def inner(*args, **kwargs): #1
... print "Arguments were: %s, %s" % (args, kwargs)
... return func(*args, **kwargs) #2
... return inner
#1でinner関数は任意の個数, 形式のパラメータを取ることができ, #2でそれをアンパックして引数として渡すことができていることに注目してください. これによりどのような関数に対してもデコレータloggerを適用することができます.

>>> @logger
... def foo1(x, y=1):
... return x * y
>>> @logger
... def foo2():
... return 2
>>> foo1(5, 4)
Arguments were: (5, 4), {}
20
>>> foo1(1)
Arguments were: (1,), {}
1
>>> foo2()
Arguments were: (), {}
2
より詳細が知りたい人のために

最後の例が理解できていれば, あなたはデコレータについて理解できたことになります! わからなければ前のステップに戻って何度でも読み返してみましょう.
もっと学習したい方はBruce Eckelの書いた下記素晴らしいエッセイを読むといいでしょう.

Decorators I: Introduction to Python Decorators
Python Decorators II: Decorator Arguments

(*)callableとは引数を取って結果を返す, 呼び出し可能なオブジェクトの総称を指します. functionはもちろんclassやmethod, generatorなどもcallableです.

2014年12月24日水曜日

発音基礎は「ア・エ・イ・ウ・エ・オ・ア・オ」

 最初に「何ができないのかを分かる」ための滑舌トレーニングします。「発音基礎」や「早口言葉」を声に出して読み、自らの声に耳を傾けてください。前回練習した発声も意識してくださいね。

滑舌トレーニング(発音基礎)

 まずは「ア・エ・イ・ウ・エ・オ・ア・オ」から始めましょう。

 アナウンサーや役者の発音練習は、「ア・イ・ウ・エ・オ」ではなく「ア・エ・イ・ウ・エ・オ・ア・オ」と声を出します。この配列は、滑舌を良くし、正しい口の形を意識できるのです。

 練習法は3段階です。苦手な音や行があったら、チェックしてください。

1 ア、エ、イ、ウ、エ、オ、ア、オ

一音一音を区切って、リズミカルにはっきり発音します

2 ア〜エ〜イ〜ウ〜エ〜オ〜ア〜オ〜

全ての母音を伸ばしつつ、一息で発音します。

3 アエイウエオアオ

最初はゆっくり、だんだん早くして、早口言葉のように一息で発音します

 次のリストを、それぞれ3段階のパターンで発声しましょう。

アエイウエオアオ   カケキクケコカコ

サセシスセソサソ   タテチツテトタト

ナネニヌネノナノ   ハヘヒフヘホハホ

マメミムメモマモ   ヤエイユエヨヤヨ

ラレリルレロラロ   ワエイウエヲワヲ

ガゲギグゲゴガゴ   カ゜ケ゜キ゜ク゜ケ゜コ゜カ゜コ゜

ザゼジズゼゾザゾ   ダデヂヅデドダド

バベビブベボバボ   パペピプペポパポ

 「カ゜ケ゜キ゜ク゜ケ゜コ゜カ゜コ゜」という見慣れない表記があります。

 これは「鼻濁音(びだくおん)」です。「ガギグゲゴ」をそのまま発音すると耳障りなので、アナウンサーや役者など話のプロは、ガ行を鼻に抜いて柔らかく発音します。

 原則、単語の中や末尾のガ行は鼻濁音で発音します。「映画館(エイカ゜カン)」「音楽(オンカ゜ク)」「賞味期限(ショウミキケ゜ン)」などです。他にも「私が〜○○したのですが〜」の「が」は「カ゜(鼻濁音)」です。

 鼻濁音が分からない方は、ニュースやナレーションを注意深く聞いてみましょう。お薦めなのは、フジテレビ系列の朝の情報番組「めざましテレビ」の軽部真一アナウンサーです。軽部さんは、鼻濁音を明確に出しているので、良いお手本になります。

 今回の映像では、割り箸を使ったトレーニングを紹介します。

 目的は、舌の動きをチェックすることと、割り箸をくわえて負荷をかけることで、口の周りと舌の筋肉を鍛えることです。スポーツジムで筋肉を付けるためにバーベルやマシンで負荷をかけるのと同じ仕組みです。

 声帯、口の周り、舌もそれぞれ筋肉です。繰り返し練習すれば、必ず努力に応えてくれますよ。

滑舌トレーニング(早口言葉)

 発音トレーニングで思い浮かぶのが、「早口言葉」です。アナウンサーが早口言葉にチャレンジするシーンを、テレビなどで見たことがある人も多いでしょう。

 早口言葉は、最初から早く話すのではなく、まずは「ゆっくりと」「一語一語」「明確に音を出すこと」を意識することから始めます。苦手な文章があれば、チェックします。慣れてきたら、だんだん早くしてみましょう。

あやしみと、あやしむべきを、あやしまず。あやしからぬを、あやしむ、あやし

川から上がった河童さんは、皿が乾いたから、頭がなかなか働かなかった。何だか頭がガンガンなった

歌うたいが、歌うたいに来て、歌うたえというが、歌うたいのように、歌うたえれば歌うたうが、歌うたいのようには歌うたえぬから、歌うたわぬ

お客が柿ョむきゃ飛脚が柿ョ食う。飛脚が柿ョむきゃお客が柿ョ食う。お客も飛脚もよく柿ョ食う飛脚

みみずにょろにょろ、三にょろにょろ。合わせてにょろにょろ、六にょろにょろ

カエルぴょこぴょこ、三ぴょこぴょこ。合わせてぴょこぴょこ、六ぴょこぴょこ

踊りおどるなら、踊りの道理を習って、踊りの道理通りに、踊りをおどれ

とろろ汁をとる苦労より、とろろ汁からとろっとする、とろろ汁をとる苦労

笑わば笑え、わらわは笑われるいわれはないわい

わしの家のわしの木に、鷲が止まったから、わしが鉄砲で撃ったら、鷲も驚いたがわしも驚いた

 ポイントは、最初から最後まで「同じ速度」で「同じ声の大きさ」で読むことです。実は、人は苦手な発音や言葉の組み合わせになると、無意識に話す速度が速くなり、声も小さくなりがちなのです。

 「滑舌に自信がないけれど一人では確認しにくい」という場合は、ボイスレコーダーに録音することをお勧めします。スマートフォンの録音機能を利用すると簡単です。

 想像していた声とイメージが違うことが多いため、自分の声を聞きたくないという方も多いようです。しかし、自分の声を客観的に知ることは、とても効果的なのです。「あ、こういう風に聞こえるのか」「あれー、イメージと違うな」などが分かります。また、トレーニングを重ねたら、最初に録音した声と比較すると、声の変化を実感できます。