2011-07-18 41 views
29

拡張メソッドをpython組み込み型に追加することは可能ですか? 私は、単に新しいメソッドを追加することで、定義された型に拡張メソッドを追加できることを知っています。Pythonビルトイン型の拡張メソッド!

class myClass: 
    pass 

myClass.myExtensionMethod = lambda self,x:x * 2 
z = myClass() 
print z.myExtensionMethod(10) 

しかし、私はすべての組み込みかなり確信しているので、ありません任意のリストのようなタイプのbuilt'inのpythonに拡張メソッドを追加する方法、辞書、...

list.myExtension = lambda self,x:x * 2 
list.myExtension(10) 
+1

サイドノート:これはルビーが許しています。これをサポートする他の言語はありますか? –

+0

Karoly:明らかにスモールトーク: – Wrameerez

+1

可能な複製[Pythonでコアタイプの猿のパッチメソッドを使えますか?](http://stackoverflow.com/questions/192649/can-you-monkey-patch-methods-on-core -types-in-python) – jfs

答えて

54

それは、この信じられないほど巧妙なモジュールで、純粋なPythonで行うことができます。たとえば

https://pypi.python.org/pypi/forbiddenfruit

import functools 
import ctypes 
import __builtin__ 
import operator 

class PyObject(ctypes.Structure): 
    pass 

Py_ssize_t = hasattr(ctypes.pythonapi, 'Py_InitModule4_64') and ctypes.c_int64 or ctypes.c_int 

PyObject._fields_ = [ 
    ('ob_refcnt', Py_ssize_t), 
    ('ob_type', ctypes.POINTER(PyObject)), 
] 

class SlotsPointer(PyObject): 
    _fields_ = [('dict', ctypes.POINTER(PyObject))] 

def proxy_builtin(klass): 
    name = klass.__name__ 
    slots = getattr(klass, '__dict__', name) 

    pointer = SlotsPointer.from_address(id(slots)) 
    namespace = {} 

    ctypes.pythonapi.PyDict_SetItem(
     ctypes.py_object(namespace), 
     ctypes.py_object(name), 
     pointer.dict, 
    ) 

    return namespace[name] 

def die(message, cls=Exception): 
    """ 
     Raise an exception, allows you to use logical shortcut operators to test for object existence succinctly. 

     User.by_name('username') or die('Failed to find user') 
    """ 
    raise cls(message) 

def unguido(self, key): 
    """ 
     Attempt to find methods which should really exist on the object instance. 
    """ 
    return functools.partial((getattr(__builtin__, key, None) if hasattr(__builtin__, key) else getattr(operator, key, None)) or die(key, KeyError), self) 

class mapper(object): 
    def __init__(self, iterator, key): 
     self.iterator = iterator 
     self.key = key 
     self.fn = lambda o: getattr(o, key) 

    def __getattribute__(self, key): 
     if key in ('iterator', 'fn', 'key'): return object.__getattribute__(self, key) 
     return mapper(self, key) 

    def __call__(self, *args, **kwargs): 
     self.fn = lambda o: (getattr(o, self.key, None) or unguido(o, self.key))(*args, **kwargs) 
     return self 

    def __iter__(self): 
     for value in self.iterator: 
      yield self.fn(value) 

class foreach(object): 
    """ 
     Creates an output iterator which will apply any functions called on it to every element 
     in the input iterator. A kind of chainable version of filter(). 

     E.g: 

     foreach([1, 2, 3]).__add__(2).__str__().replace('3', 'a').upper() 

     is equivalent to: 

     (str(o + 2).replace('3', 'a').upper() for o in iterator) 

     Obviously this is not 'Pythonic'. 
    """ 
    def __init__(self, iterator): 
     self.iterator = iterator 

    def __getattribute__(self, key): 
     if key in ('iterator',): return object.__getattribute__(self, key) 
     return mapper(self.iterator, key) 

    def __iter__(self): 
     for value in self.iterator: 
      yield value 

proxy_builtin(list)['foreach'] = property(foreach) 

import string 

print string.join([1, 2, 3].foreach.add(2).str().add(' cookies').upper(), ', ') 

>>> 3 COOKIES, 4 COOKIES, 5 COOKIES 

ここで気分がいいですか?

+3

これは、The Empire Strikes Backの[そのシーン](http://imgur.com/lVEdIXO)の1つを思い出させます。 – n611x007

+15

この回答のすべてのコードは何ですか?ちょうど 'pip install forbiddenfruit'を行い、その後 'frombiddenfruit install curse'を実行して、宇宙を破壊しようとします。 – ArtOfWarfare

+0

@ArtOfWarfare彼はクッキーを追加しました。 – MaLiN2223

3

で次のように-inの型は最適化されたCで書かれているため、Pythonで変更することはできません。私が試してみると、私はちょうど得る:

TypeError: can't set attributes of built-in/extension type 'list' 
16

番号は、monkeypatchedすることはできません。

+1

それは悲しいですが、本当です。私は通常、組み込み関数から継承し、サルは必要になった場合、子クラスにパッチを当てます。 – Ishpeck

+4

@Ishpeck:それは普通のサブクラス化のように聞こえますが、サルのパッチ適用はまったくありません。 –

+2

これは通常、サブクラスのインスタンスに対して行われた、単なるキー操作です。そして、それは非常に重いです。これほどのことはしないでください、子供たち。 – Ishpeck

2

あなたができることは、組み込み型からクラスを派生しているようです。たとえば:

class mylist(list): 
    def myfunc(self, x): 
     self.append(x) 

test = mylist([1,2,3,4]) 
test.myfunc(99) 

(同じコンストラクタを取得するように、あなたが望むならばあなたも、「リスト」に名前を付けることができます。)しかし、あなたは直接あなたの質問の例のようなビルトインタイプを変更することはできません。

12

いいえ、サブクラス化してください!

>>> import string 
>>> class MyString(str): 
...  def disemvowel(self): 
...   return MyString(string.translate(self, None, "aeiou")) 
... 
>>> s = MyString("this is only a test") 
>>> s.disemvowel() 
'ths s nly tst' 

以上の具体的なあなたの例に

>>> class MyList(list): 
...  pass 
... 
>>> MyList.myExtension = lambda self,x:x * 2 
>>> l = MyList() 
>>> l.myExtension(10) 
20