こんにちは、Retroidです。
過去記事にも書いてあるように、Fluent Pythonを読んでいるのですが、解説が細かくて、「なるほど、こういう意味なのか!」と感心させられることが多々ありました。
というわけで、Fluent Pythonで学んでためになったことを、これから度々書いていこうと思います。
著作権について
これから備忘録には、サンプルコードを示していきます。
Fluent Pythoのコードは、GitHubにてMITライセンスで公開されています。
おかげさまで安心して引用できます(^^)
※MITライセンスのコードは、コピーライトをしっかり貼れば、ほとんど制限なく利用できます。
免責
自分の勉強も兼ねているので、正確な知識を頭に入れて文章を書くように努めておりますが、間違っていた場合は教えてください。
不利益が出ても責任負えません(^_^;)💧
頑張って正確な記事を書けるように努めますm(_ _)m
ジェネリック関数について
「ジェネリック」というと、芸能界の重鎮、黒柳徹子様が宣伝している「ジェネリック医薬品」が思い浮かびます。
このような場合のジェネリックとは、「後発の会社が開発した、特許切れの商品と同じような効果を持つ商品のこと」と認識しております。
一方で、プログラミングにおける、ジェネリック関数は、「第一引数の型に応じて、同じような処理を異なった方法で行う関数の集まり」です。
例えば、Pythonの型には、
- 数値
- シーケンス
- マッピング
- クラス
- インスタンス
- 例外
といったものがあります。
以下の例では、
- str型(シーケンス型の一種)
- numbers.Integral(intの仮想スーパークラス)
- tuple型
- abc.MutableSequence(ミュータブルなシーケンス型)
- obj(Pythonでは全てがオブジェクトなので、多分↑4つが当てはまらなかったときは全部これで処理される?)
が第一引数として投げ込まれた際に、1つの関数(htmlize)へ投げ込んだにもかかわらず、投げ込まれた引数の型に応じて、それぞれの型に対応する関数へ振り分けられます。
なんで、int型じゃなくてnumbers.Integralなんだ?
とか
なんで、list型じゃなくてabc.MutableSequence型なんだ?
という疑問が出てくると思いますが、これは、intやlistのみならず、intやlistのベースとなったクラスより生成されたオブジェクトについても、処理の振り分けができるようにするためです。
つまり、このジェネリック関数は、int型やlist型はもちろん、互換性のある型についても、ちゃんと分けて処理できるってことです。
抽象基底クラスというものを理解すると良いらしいですが、retroidも現在勉強中の分野のため、このくらいにしておきます(^_^;)
さて、サンプルコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
from functools import singledispatch from collections import abc import numbers import html @singledispatch def htmlize(obj): content = html.escape(repr(obj)) return '<pre>{}</pre>'.format(content) @htmlize.register(str) def _(text): content = html.escape(text).replace('\n', '<br>\n') return '<p>{0}</p>'.format(content) @htmlize.register(numbers.Integral) def _(n): return '<pre>{0} (0x{0:x})</pre>'.format(n) @htmlize.register(tuple) @htmlize.register(abc.MutableSequence) def _(seq): inner = '</li>\n<li>'.join(htmlize(item) for item in seq) return '<ul>\n<li>' + inner + '</li>\n</ul>' |
import htmlは、もともとPythonに用意されているモジュールです。
文字列の &、<、および > を HTML セーフなシーケンスに変換するとのこと。
①⇒singledispatchでhtmlize関数をジェネリック関数にしている。
②⇒str型の引数が入った時に実行される関数
③⇒numbers.Integralが引数として入った時に実行される関数
④⇒tuple型または、abc.MutableSequenceが入ったときに実行される関数
って感じになります。
①のhtmlize関数の前に@singledispatchを入れることによって、htmlize関数をジェネリック関数にしています。
①自体は、object型を処理する関数です。
若干推測入るのですが、もし②−④までの条件が当てはまらなかったら①で処理されるのかな?と思いました。
不明瞭ですみません。
わかったら追記します。
というわけで、Pythonにおけるジェネリック関数の簡単な説明と、Pythonでジェネリック関数を使うには、@singledispatchを使えば良いよ!って話をしました。
自分も関数書いてて、「引数に入ってくる文字型ごとに処理を変えたいなぁ」と思うことが結構ありました。
これからは、@singledispatchをうまく使ってスマートなコードを書いていきたいと思います。
サンプルコードのライセンス
The MIT License (MIT)
Copyright (c) 2014 Luciano Ramalho
https://github.com/fluentpython/example-code/blob/master/LICENSE
参考文献
Fluent Python