2011-07-18 137 views
29

是否可以將擴展方法添加到python內置類型? 我知道我可以通過添加新方法來將擴展方法添加到已定義的類型。如下:python內置類型的擴展方法!

class myClass: 
    pass 

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

但就是沒有辦法加入擴展方法蟒蛇built'in類型,如列表,字典,...

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

旁註:紅寶石允許這樣做。任何其他支持這種語言的語言? –

+0

Karoly:Smalltalk顯然:) – Wrameerez

+1

可能重複的[你可以猴子補丁方法的核心類型在Python?](http://stackoverflow.com/questions/192649/can-you-monkey-patch-methods-on-core -pypes-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

這提醒了帝國反擊中的[那個場景](http://imgur.com/lVEdIXO)...... – n611x007

+15

這個答案中的所有代碼究竟是什麼?只要做'pip安裝forbiddenfruit',然後'從禁止的水果安裝詛咒'並着手破壞宇宙。 – ArtOfWarfare

+0

@ArtOfWarfare他添加了cookies,所以我認爲這是值得的 – MaLiN2223

3

沒有,因爲我敢肯定,所有的內置-in類型是用優化的C編寫的,因此不能用Python修改。當我嘗試它時,我只會得到:

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

否。在C中定義的類型不能被monkeypatched。

+1

這很傷心,但是是真的。如果有必要,我通常最終會從內建中繼承,而猴子則會修補子類。 – Ishpeck

+4

@Ishpeck:聽起來像普通的子類,根本不是猴子補丁。 –

+2

它通常是monkeypatching,只是做了子類的實例。它是十分糟糕的。永遠別做,孩子。 – 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