2011-07-18 11 views
29

Czy można dodać metodę rozszerzenia do wbudowanych typów Pythona? Wiem, że mogę dodać metodę rozszerzenia do zdefiniowanego typu, po prostu dodając nową metodę przez. w następujący sposób:Metoda rozszerzenia dla wbudowanych typów Pythona!

class myClass: 
    pass 

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

Ale jest jakiś sposób, aby dodać metodę rozszerzenia do pytona built'in typów, takich jak listy, dict ...

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

sidenote: ruby ​​na to pozwala. jakieś inne języki, które wspierają to? –

+0

Karoly: Smalltalk w sposób oczywisty :) – Wrameerez

+1

możliwy duplikat metod patch [Czy potrafisz łączyć małpy na podstawowych typach w pythonie?] (Http://stackoverflow.com/questions/192649/can-you-monkey-patch-methods-on-core -types-w-python) – jfs

Odpowiedz

54

Można to zrobić w czystym Pythonie z tym niezwykle sprytny modułu:

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

Na przykład:

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 

Czy to nie jest dobre?

+3

To przypomina jedną z [tej sceny] (http://imgur.com/lVEdIXO) w Imperium kontratakuje ... – n611x007

+15

Co na ziemi jest cały kod w tej odpowiedzi? Po prostu zrób 'pip install forbiddenfruit', następnie' from forbiddenfruit install curse' i zabierz się za niszczenie wszechświata. – ArtOfWarfare

+0

@ArtOfWarfare dodał ciasteczka, więc myślę, że było warto – MaLiN2223

3

Nie, bo jestem całkiem pewny, że wszystkie wbudowane -w typy są napisane w zoptymalizowanym C, więc nie można go modyfikować za pomocą Pythona. Gdy próbuję, otrzymuję:

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

Nr Typów zdefiniowanych w C nie można monkeypatched.

+1

To smutne, ale prawdziwe. Zwykle kończę dziedziczenie z wbudowanej i małpiej łatki klasy dziecka, jeśli będzie to konieczne. – Ishpeck

+4

@Ishpeck: To brzmi jak zwykła podklasa, a nie łatka małpy. –

+2

Zazwyczaj jest to monkeypatching, zrobione tylko na instancję podklasy. A to jest okrutne i złe. Nigdy tego nie rób, dzieci. – Ishpeck

2

Wydaje się, że najlepsze, co można zrobić, pochodzi z klasy wbudowanej. Na przykład:

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

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

(. Można nawet nazwać „List”, tak aby uzyskać ten sam konstruktor, jeśli chcesz), jednak nie można bezpośrednio modyfikować wbudowany typu jak na przykład w swoim pytaniu.

12

Nie, musisz podklasy!

>>> 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' 

Albo bardziej specyficzne dla przykładu

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