2011-07-18 87 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