SlideShare a Scribd company logo
第n回
XXX勉強会
小倉 大樹
メタプログラミングによる
高次的コーディング技術
の
基礎を学ぶ
黒魔術入門
The Black Magic
メタプログラミングとは
ですが
大雑把に言えば
「コードを生成するコード」
「ロジックを生成するロジック」
「プログラミングそのものをプログラミングする行為」
のこと
なるほどわからん
なのでタイトルも「メタプログラミング入門」から「黒魔術入門」に変更しました
厳密に定義してスライドを作るのは面倒なので、
!
今回のスライドでは
「コーディングそのものに関わる抽象的な言語機能」
を総称してメタプログラミングと呼びます。
Caution!
多用厳禁!
メタプログラミングはロジックが抽象的で難解になる
場合によってはエラーの原因を追いにくくなる
意味不明なエラーが発生する
どういう背景で動作しているのか理解しにくくなる
そのメタプロ、必要ですか?
うまく使えば強い武器になる。
特に、RubyやPythonを使うのであれば
いずれも必須の知識(だと筆者は考える)。
Web Application FrameWork, ORMapperなどの大規模なライブラリで
あれば必ず使われているテクニック。
難解なメタレベルコーディングで厨二心を満たせ!
とはいえ
説明の前に
諸注意
黒魔術が比較的易しい and 頻出 and メジャーな言語、などの理由から
今回の説明に使う言語はPythonです(筆者の知識の問題もあります)。
マクロについては、筆者のLisp力や抽象構文木に対する理解度不足などの
事情があるので今回は取り扱いません。
比較的に易しい内容を取り扱う予定ですが、最低限のオブジェクト指向と
その言語内実装に対する理解度はあった方が良いかもしれません。
Ghost Method 	

動的なメソッド呼び出し
Ghost Method
サンプルコード(動的な委譲)
>>> class DynamicProxy(object):
... def __init__(self, value):
... self._value = value
...
... def __getattr__(self, name):
... return getattr(self._value, name)
...
!
>>> proc = DynamicProxy('hoge')
>>> proc.title()
'Hoge'
Ghost Method
サンプルコード解説
def __getattr__(self, name):
pass
__getattr__(Rubyではmethod_missing)を
実装したクラスに対して、実装されていないメソッド名での
呼び出しが行われた時に呼び出される。
name引数には呼ばれたときの名前が入る。
Ghost Method
動的ディスパッチ解説
getattr(obj, name)
getattr関数(RubyではObject#send)
対象オブジェクトに対して、与えられた文字列名での
メソッド呼び出しを行う。
sendという名前はSmallTalkを意識してつけられたものか?
Ghost Method
利用例
ORMapperのクエリビルダ
find_by_column_nameをパースしてクエリを生成する……など
!
APIクライアントの実装
APIを変更しても、ソースコードをいじる必要がなくなるような実装が可能
!
その他、使い道色々。
高階関数	

関数型のプログラミングスタイル
メタプロと読んで良いかどうかは微妙だが、
ロジックを動的に生成するロジックとしては頻出する
サンプルコード(動的な委譲)
>>> from operator import methodcaller
!
>>> class NullSafeContainer(object):
... def __getattr__(self, name):
... def _(targ):
... if targ is None:
... return lambda *args, **kw: None
... return lambda *args, **kw: methodcaller(name,
*args, **kw)(targ)
... return _
...
>>> ns = NullSafeContainer()
>>> ns.replace('foo bar baz')('bar', 'gege')
'foo gege baz'
>>> ns.replace(None)('bar', 'gege')
サンプルコードの解説
Ghost Methodと組み合わせて、NullSafeな動的委譲を行う
プロキシを実装している。
JavaScriptの勉強会でも書いたが、高階関数とレキシカルスコープ自体は
極基礎的な知識なので覚えよう。
Monkey Patch	

オープンクラス
Monkey Patch
サンプルコード(動的な委譲)
>>> class Container(object):
... pass
...
>>> c = Container()
!
>>> def new_method(self, val):
... return val
...
>>> # setattr(Container, 'new_method', new_method)
>>> Container.new_method = new_method
>>> c.new_method('new!')
'new!'
Monkey Patch
解説
オープンクラスなどとも呼ばれ、実行時にクラスの実装を拡張するような機能。
ダックタイピングと組み合わせて使う強力な技。
しかし多用するとプロジェクトがカオスに陥る大変危険な機能であり、
Pythonでは利用しにくくなっている。
しかし、後述するメタクラスによる動的拡張などを行うための
基礎的な知識となる。
他にも、テスト系のライブラリではよく利用される。
以下蛇足
Pythonではインスタンスへの動的なメソッド追加は出来ない。
>>> def new_instance_method(self, val):
... return 'in instance "{0}"'.format(val)
...
>>> # c.new_instance_method = new_instance_method
>>> setattr(c, 'new_instance_method', new_instance_method)
>>> c.new_instance_method('new!')
Traceback (most recent call last):
...
TypeError: new_instance_method() takes exactly 2 arguments
(1 given)
以下蛇足
しかしインターフェースを整えることは出来る
>>> def demi_instance_dispatcher(instance, method):
... setattr(instance, method.__name__, lambda *args,
**kw: method(instance, *args, **kw))
...
>>> demi_instance_dispatcher(c, new_instance_method)
>>> c.new_instance_method('new!')
'in instance "new!"'
Descriptor	

ディスクリプター
Descriptor
解説
Rubyに同様の機能が存在するかどうかは知らない。
オブジェクトの属性アクセス時の振る舞いをカスタマイズ出来るようになる機能。
property, classmethodなどの機能はこれで実現出来る。
時間がないので詳しい説明は割愛。
Meta Class	

黒魔術っぽくなってきた
Meta Class その前に
クラスの動的な生成
>>> MyClass = type(
... 'MyClass',
... (object, ),
... {
... '__init__': lambda self, val: setattr(self,
'_val', val),
... 'say': lambda self: self._val,
... },
... )
>>> mc = MyClass('aaa')
>>> mc.say()
'aaa'
クラスの動的生成
解説
Pythonでは、type組み込み関数を使うことでクラスを動的に定義出来る。
class構文は、内部的には上記のtypeを呼び出しているのと同じだと言える。
そして、classが内部的に呼び出しているtypeを
別の関数で置き換えることが出来る。
!
この機能がメタクラスである。
メタクラスの構文
>>> class MetaClassExample(type):
... def __new__(cls, name, bases, dict):
... return type.__new__(cls, name, bases, dict)
...
>>> class Klass(object):
... __metaclass__ = MetaClassExample
何もしない例
メタクラスの構文
解説
Python2では __metaclass__ クラスメンバ。
Python3では class SomeClass(metaclass=SomeMetaClass): のように定義。
例にある通り、
nameにはクラス名、
basesには基底クラス群、
dictにはオブジェクトの属性がそれぞれ入っている。
あとは好きにカスタマイズすれば良いわけです。
メタクラスの活用例
>>> def _logging_hook(func):
... def __(*args, **kw):
... print('before: {0}'.format(func.__name__))
... ret = func(*args, **kw)
... print('after: {0}'.format(func.__name__))
... return ret
... return __
...
次ページへつづく
こういう関数を用意しておいて……
>>> from types import (
... MethodType, FunctionType, LambdaType
... )
>>> class LoggingHookKlass(type):
... def __new__(cls, name, bases, dict):
... hooked_attrs = {
... k: _logging_hook(v)
... for k, v in dict.items()
... if isinstance(v, (MethodType, FunctionType,
LambdaType))
... }
... return type.__new__(cls, name, bases, hooked_attrs)
次ページへつづく
こういうメタクラスを作って
>>> class Ninja(object):
... __metaclass__ = LoggingHookKlass
... def aisatsu(self, other):
... print(‘ドーモ、{0}=サン’.format(other))
...
>>> ninja = Ninja()
>>> ninja.aisatsu('オフェンダー')
before: aisatsu
ドーモ、オフェンダー=サン
after: aisatsu
次ページで解説
こう使う
メタクラス
解説
レシーバとして受け取った関数の実行前後にロギング(print文)を
実行する関数を容易して、
メタクラスを使って対象クラスの生成時に、全てのメソッドに対して
前述のロギングするラッパー関数をかませている。
ね、簡単でしょ?
メタクラス
解説
出来ることは非常に多い。
が、サンプルのようなことをするのであればデコレータで良い。
使いどころを考えさせられる機能と言える。
より実践的な例だと
https://github.com/hachibeeDI/forwardable.py
とかが参考になるかも?
おしまい
用法用量を守って楽しいコーディングを

More Related Content

What's hot

メタプログラミングって何だろう
メタプログラミングって何だろうメタプログラミングって何だろう
メタプログラミングって何だろうKota Mizushima
 
DockerとPodmanの比較
DockerとPodmanの比較DockerとPodmanの比較
DockerとPodmanの比較
Akihiro Suda
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるな
Kentaro Matsui
 
フロー効率性とリソース効率性について #xpjug
フロー効率性とリソース効率性について #xpjugフロー効率性とリソース効率性について #xpjug
フロー効率性とリソース効率性について #xpjug
Itsuki Kuroda
 
DockerコンテナでGitを使う
DockerコンテナでGitを使うDockerコンテナでGitを使う
DockerコンテナでGitを使う
Kazuhiro Suga
 
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)
NTT DATA Technology & Innovation
 
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計
Yoshinori Matsunobu
 
ブルックスのいう銀の弾丸とは何か?
ブルックスのいう銀の弾丸とは何か?ブルックスのいう銀の弾丸とは何か?
ブルックスのいう銀の弾丸とは何か?
Yoshitaka Kawashima
 
Python 3.9からの新定番zoneinfoを使いこなそう
Python 3.9からの新定番zoneinfoを使いこなそうPython 3.9からの新定番zoneinfoを使いこなそう
Python 3.9からの新定番zoneinfoを使いこなそう
Ryuji Tsutsui
 
Mavenの真実とウソ
Mavenの真実とウソMavenの真実とウソ
Mavenの真実とウソ
Yoshitaka Kawashima
 
何となく勉強した気分になれるパーサ入門
何となく勉強した気分になれるパーサ入門何となく勉強した気分になれるパーサ入門
何となく勉強した気分になれるパーサ入門
masayoshi takahashi
 
Test Yourself - テストを書くと何がどう変わるか
Test Yourself - テストを書くと何がどう変わるかTest Yourself - テストを書くと何がどう変わるか
Test Yourself - テストを書くと何がどう変わるか
Takuto Wada
 
Cognitive Complexity でコードの複雑さを定量的に計測しよう
Cognitive Complexity でコードの複雑さを定量的に計測しようCognitive Complexity でコードの複雑さを定量的に計測しよう
Cognitive Complexity でコードの複雑さを定量的に計測しよう
Shuto Suzuki
 
PostgreSQLアンチパターン
PostgreSQLアンチパターンPostgreSQLアンチパターン
PostgreSQLアンチパターン
Soudai Sone
 
世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture
Atsushi Nakamura
 
ネットワーク ゲームにおけるTCPとUDPの使い分け
ネットワーク ゲームにおけるTCPとUDPの使い分けネットワーク ゲームにおけるTCPとUDPの使い分け
ネットワーク ゲームにおけるTCPとUDPの使い分け
モノビット エンジン
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法について
Yuji Otani
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
Kohei Tokunaga
 
デキるプログラマだけが知っているコードレビュー7つの秘訣
デキるプログラマだけが知っているコードレビュー7つの秘訣デキるプログラマだけが知っているコードレビュー7つの秘訣
デキるプログラマだけが知っているコードレビュー7つの秘訣
Masahiro Nishimi
 
マイクロサービスにおける 結果整合性との戦い
マイクロサービスにおける 結果整合性との戦いマイクロサービスにおける 結果整合性との戦い
マイクロサービスにおける 結果整合性との戦い
ota42y
 

What's hot (20)

メタプログラミングって何だろう
メタプログラミングって何だろうメタプログラミングって何だろう
メタプログラミングって何だろう
 
DockerとPodmanの比較
DockerとPodmanの比較DockerとPodmanの比較
DockerとPodmanの比較
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるな
 
フロー効率性とリソース効率性について #xpjug
フロー効率性とリソース効率性について #xpjugフロー効率性とリソース効率性について #xpjug
フロー効率性とリソース効率性について #xpjug
 
DockerコンテナでGitを使う
DockerコンテナでGitを使うDockerコンテナでGitを使う
DockerコンテナでGitを使う
 
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)
 
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計
 
ブルックスのいう銀の弾丸とは何か?
ブルックスのいう銀の弾丸とは何か?ブルックスのいう銀の弾丸とは何か?
ブルックスのいう銀の弾丸とは何か?
 
Python 3.9からの新定番zoneinfoを使いこなそう
Python 3.9からの新定番zoneinfoを使いこなそうPython 3.9からの新定番zoneinfoを使いこなそう
Python 3.9からの新定番zoneinfoを使いこなそう
 
Mavenの真実とウソ
Mavenの真実とウソMavenの真実とウソ
Mavenの真実とウソ
 
何となく勉強した気分になれるパーサ入門
何となく勉強した気分になれるパーサ入門何となく勉強した気分になれるパーサ入門
何となく勉強した気分になれるパーサ入門
 
Test Yourself - テストを書くと何がどう変わるか
Test Yourself - テストを書くと何がどう変わるかTest Yourself - テストを書くと何がどう変わるか
Test Yourself - テストを書くと何がどう変わるか
 
Cognitive Complexity でコードの複雑さを定量的に計測しよう
Cognitive Complexity でコードの複雑さを定量的に計測しようCognitive Complexity でコードの複雑さを定量的に計測しよう
Cognitive Complexity でコードの複雑さを定量的に計測しよう
 
PostgreSQLアンチパターン
PostgreSQLアンチパターンPostgreSQLアンチパターン
PostgreSQLアンチパターン
 
世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture
 
ネットワーク ゲームにおけるTCPとUDPの使い分け
ネットワーク ゲームにおけるTCPとUDPの使い分けネットワーク ゲームにおけるTCPとUDPの使い分け
ネットワーク ゲームにおけるTCPとUDPの使い分け
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法について
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
 
デキるプログラマだけが知っているコードレビュー7つの秘訣
デキるプログラマだけが知っているコードレビュー7つの秘訣デキるプログラマだけが知っているコードレビュー7つの秘訣
デキるプログラマだけが知っているコードレビュー7つの秘訣
 
マイクロサービスにおける 結果整合性との戦い
マイクロサービスにおける 結果整合性との戦いマイクロサービスにおける 結果整合性との戦い
マイクロサービスにおける 結果整合性との戦い
 

Recently uploaded

シグネチャで始めるRustプログラミング - Superteam Japan Developer Event
シグネチャで始めるRustプログラミング - Superteam Japan Developer Eventシグネチャで始めるRustプログラミング - Superteam Japan Developer Event
シグネチャで始めるRustプログラミング - Superteam Japan Developer Event
K Kinzal
 
クラウドネイティブにおけるセキュアなソフトウェア・サプライ・チェーンの考え方とベストプラクティス.pdf
クラウドネイティブにおけるセキュアなソフトウェア・サプライ・チェーンの考え方とベストプラクティス.pdfクラウドネイティブにおけるセキュアなソフトウェア・サプライ・チェーンの考え方とベストプラクティス.pdf
クラウドネイティブにおけるセキュアなソフトウェア・サプライ・チェーンの考え方とベストプラクティス.pdf
TatsuyaHanayama
 
RaySheetで解決できるシナリオ10選-業務改善に貢献する機能 - RaySheet Documentation
RaySheetで解決できるシナリオ10選-業務改善に貢献する機能 - RaySheet DocumentationRaySheetで解決できるシナリオ10選-業務改善に貢献する機能 - RaySheet Documentation
RaySheetで解決できるシナリオ10選-業務改善に貢献する機能 - RaySheet Documentation
GrapeCity, inc.
 
RaySheet Product Description Documentation - 2024.6.19
RaySheet Product Description Documentation - 2024.6.19RaySheet Product Description Documentation - 2024.6.19
RaySheet Product Description Documentation - 2024.6.19
GrapeCity, inc.
 
Solanaで始めるRustプログラミング - Superteam Japan Developer Event
Solanaで始めるRustプログラミング - Superteam Japan Developer EventSolanaで始めるRustプログラミング - Superteam Japan Developer Event
Solanaで始めるRustプログラミング - Superteam Japan Developer Event
K Kinzal
 
GPT - 振り返りフレームワークKPTをちょっとKAIZENしてちょうど良いフレームワークに。
GPT - 振り返りフレームワークKPTをちょっとKAIZENしてちょうど良いフレームワークに。GPT - 振り返りフレームワークKPTをちょっとKAIZENしてちょうど良いフレームワークに。
GPT - 振り返りフレームワークKPTをちょっとKAIZENしてちょうど良いフレームワークに。
Hibiki Mizuno
 
RayBarcode Product Description Documentation - 2024.6.19
RayBarcode Product Description Documentation - 2024.6.19RayBarcode Product Description Documentation - 2024.6.19
RayBarcode Product Description Documentation - 2024.6.19
GrapeCity, inc.
 
Bhyve Management Daemon Version 3.0 on FreBSD
Bhyve Management Daemon Version 3.0 on FreBSDBhyve Management Daemon Version 3.0 on FreBSD
Bhyve Management Daemon Version 3.0 on FreBSD
Yuichiro Naito
 
RayPen Product Description Documentation - 2024.6.19
RayPen Product Description Documentation - 2024.6.19RayPen Product Description Documentation - 2024.6.19
RayPen Product Description Documentation - 2024.6.19
GrapeCity, inc.
 

Recently uploaded (9)

シグネチャで始めるRustプログラミング - Superteam Japan Developer Event
シグネチャで始めるRustプログラミング - Superteam Japan Developer Eventシグネチャで始めるRustプログラミング - Superteam Japan Developer Event
シグネチャで始めるRustプログラミング - Superteam Japan Developer Event
 
クラウドネイティブにおけるセキュアなソフトウェア・サプライ・チェーンの考え方とベストプラクティス.pdf
クラウドネイティブにおけるセキュアなソフトウェア・サプライ・チェーンの考え方とベストプラクティス.pdfクラウドネイティブにおけるセキュアなソフトウェア・サプライ・チェーンの考え方とベストプラクティス.pdf
クラウドネイティブにおけるセキュアなソフトウェア・サプライ・チェーンの考え方とベストプラクティス.pdf
 
RaySheetで解決できるシナリオ10選-業務改善に貢献する機能 - RaySheet Documentation
RaySheetで解決できるシナリオ10選-業務改善に貢献する機能 - RaySheet DocumentationRaySheetで解決できるシナリオ10選-業務改善に貢献する機能 - RaySheet Documentation
RaySheetで解決できるシナリオ10選-業務改善に貢献する機能 - RaySheet Documentation
 
RaySheet Product Description Documentation - 2024.6.19
RaySheet Product Description Documentation - 2024.6.19RaySheet Product Description Documentation - 2024.6.19
RaySheet Product Description Documentation - 2024.6.19
 
Solanaで始めるRustプログラミング - Superteam Japan Developer Event
Solanaで始めるRustプログラミング - Superteam Japan Developer EventSolanaで始めるRustプログラミング - Superteam Japan Developer Event
Solanaで始めるRustプログラミング - Superteam Japan Developer Event
 
GPT - 振り返りフレームワークKPTをちょっとKAIZENしてちょうど良いフレームワークに。
GPT - 振り返りフレームワークKPTをちょっとKAIZENしてちょうど良いフレームワークに。GPT - 振り返りフレームワークKPTをちょっとKAIZENしてちょうど良いフレームワークに。
GPT - 振り返りフレームワークKPTをちょっとKAIZENしてちょうど良いフレームワークに。
 
RayBarcode Product Description Documentation - 2024.6.19
RayBarcode Product Description Documentation - 2024.6.19RayBarcode Product Description Documentation - 2024.6.19
RayBarcode Product Description Documentation - 2024.6.19
 
Bhyve Management Daemon Version 3.0 on FreBSD
Bhyve Management Daemon Version 3.0 on FreBSDBhyve Management Daemon Version 3.0 on FreBSD
Bhyve Management Daemon Version 3.0 on FreBSD
 
RayPen Product Description Documentation - 2024.6.19
RayPen Product Description Documentation - 2024.6.19RayPen Product Description Documentation - 2024.6.19
RayPen Product Description Documentation - 2024.6.19
 

Pythonによる黒魔術入門