Extension method for python built-in types

PythonForbiddenfruit

Python Problem Overview


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)

Python Solutions


Solution 1 - Python

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?

Solution 2 - Python

No. Types defined in C cannot be monkeypatched.

Solution 3 - Python

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

Solution 4 - Python

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'

Solution 5 - Python

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSaeed AfshariView Question on Stackoverflow
Solution 1 - PythontoothygooseView Answer on Stackoverflow
Solution 2 - PythonIgnacio Vazquez-AbramsView Answer on Stackoverflow
Solution 3 - PythonironchefpythonView Answer on Stackoverflow
Solution 4 - PythonfletomView Answer on Stackoverflow
Solution 5 - Pythonuser812786View Answer on Stackoverflow