is it possible to add extension method to python built-in types?
I know that I can add extension method to defined type by simply adding new method by . as following:
class myClass:
pass
myClass.myExtensionMethod = lambda self,x:x * 2
z = myClass()
print z.myExtensionMethod(10)
But is any way to adding extension method to python built’in types like list, dict, …
list.myExtension = lambda self,x:x * 2 list.myExtension(10)
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
It can be done in pure Python with this incredibly clever module:
https://pypi.python.org/pypi/forbiddenfruit
For example:
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
There, doesn’t that feel good?
Method 2
No. Types defined in C cannot be monkeypatched.
Method 3
Nope, you gotta subclass!
>>> 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'
Or more specific to your example
>>> class MyList(list): ... pass ... >>> MyList.myExtension = lambda self,x:x * 2 >>> l = MyList() >>> l.myExtension(10) 20
Method 4
No, because I’m pretty sure all the built-in types are written in optimized C and thus can’t be modified with Python. When I try it, I just get:
TypeError: can't set attributes of built-in/extension type 'list'
Method 5
The best you can do appears to be deriving a class from the built-in type. For example:
class mylist(list):
def myfunc(self, x):
self.append(x)
test = mylist([1,2,3,4])
test.myfunc(99)
(You could even name it “list” so as to get the same constructor, if you wanted.) However, you cannot directly modify a built-in type like the example in your question.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0