[Python] build-in objectに独自のメソッドを追加する禁断の果実
Pythonはクラスに後からメソッドが追加できる言語です。
class A:
pass
A().hello()
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# AttributeError: 'A' object has no attribute 'hello'
def hello(self):
print("hello")
setattr(A,'hello',hello)
A().hello()
# hello
しかし、それはあくまでpythonで定義されたクラスのみで、build-in クラスであるint
やstr
はその限りではありません。
setattr(int,'hello',hello)
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# TypeError: can't set attributes of built-in/extension type 'int'
実際にやってみるとこのようにエラーが出てしまいます。
しかし、その制限を突破してbuild-inクラスにメソッドを追加できるライブラリを発見したので記しておきます。
禁断の果実
そのライブラリはforbidden fruit
(禁断の果実)と名付けられており、いかにもな感じです。
forbiddenfruit
https://github.com/clarete/forbiddenfruit
インストール
pip install forbiddenfruit
メソッドの追加
from forbiddenfruit import curse
def rmspace(self):
return self.replace(" ", "")
curse(str, "rmspace", rmspace)
print("hello world !".rmspace())
# "helloworld !"
curse
(呪い)なんていう関数名になってます。
禁断の関数ってことですかね?
メソッドの削除
from forbiddenfruit import reverse
reverse(str,"rmspace")
print("hello world !".rmspace())
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# AttributeError: 'str' object has no attribute 'rmspace'
一方、削除する方はreverse
なので呪いをはね返すとかそんな意味なんでしょうか?
ちょっとお試し
reverse(str,"split")
"a".split()
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# AttributeError: 'str' object has no attribute 'split'
なんと、元からあるメソッドを消し去ることも出来るようです。
恐ろしい…
どんな原理?
どんな原理、方法でこのようなことを実現しているのか気になったので少し、実装を眺めてみました。
本体は、forbiddenfruit/__init__.py
のようです。
コードを貼ると大変そうなので、貼ったりはしませんが、中でctypes
モジュールを利用してcの低レベルAPIを触っているようです。
PyObject
が分かる人ならわかると思いますが、オブジェクトの持つ属性の辞書にcレベルでメソッドを追加、削除しています。
これなら同じ内容をcで書いても作れそうです(と言うかcで書いてあると思ってました)。
Python本体の実装を読んだことがある人ならある程度わかる内容ですね。
さいごに
build-in クラスにメソッドを追加できて便利そうではありますが、他の人が読んだ時に可読性が著しく低下しそうな気もするので、使い所には気をつけたいですね。