61

Does Python have extension methods like C#? Is it possible to call a method like:

MyRandomMethod()

on existing types like int?

myInt.MyRandomMethod()
Torsten Marek
  • 83,780
  • 21
  • 91
  • 98
Joan Venge
  • 315,713
  • 212
  • 479
  • 689

6 Answers6

72

You can add whatever methods you like on class objects defined in Python code (AKA monkey patching):

>>> class A(object):
>>>     pass


>>> def stuff(self):
>>>     print self

>>> A.test = stuff
>>> A().test()

This does not work on builtin types, because their __dict__ is not writable (it's a dictproxy).

So no, there is no "real" extension method mechanism in Python.

Eric
  • 95,302
  • 53
  • 242
  • 374
Torsten Marek
  • 83,780
  • 21
  • 91
  • 98
11

It can be done with Forbidden Fruit (https://pypi.python.org/pypi/forbiddenfruit)

Install forbiddenfruit:

pip install forbiddenfruit

Then you can extend built-in types:

>>> from forbiddenfruit import curse

>>> def percent(self, delta):
...     return self * (1 + delta / 100)

>>> curse(float, 'percent', percent)
>>> 1.0.percent(5)
1.05

Forbidden Fruit is fundamentally dependent on the C API, it works only on cpython implementations and won’t work on other python implementations, such as Jython, pypy, etc.

stkent
  • 19,772
  • 14
  • 85
  • 111
Rustam Ganeyev
  • 894
  • 2
  • 12
  • 29
9

not sure if that what you're asking but you can extend existing types and then call whatever you like on the new thing:

class  int(int):
     def random_method(self):
           return 4                     # guaranteed to be random
v = int(5)                              # you'll have to instantiate all you variables like this
v.random_method()

class int(int):
    def xkcd(self):
        import antigravity
        print(42)

>>>v.xkcd()
Traceback (most recent call last):
  File "<pyshell#81>", line 1, in <module>
    v.xkcd()
AttributeError: 'int' object has no attribute 'xkcd'
c = int(1)
>>> c.random_method()
4
>>> c.xkcd()
42

hope that clarifies your question

SilentGhost
  • 307,395
  • 66
  • 306
  • 293
  • But I mean calling directly on existing types, not MyInt, but int. So like v = 5; v.RandomMethod(); – Joan Venge Feb 05 '09 at 01:07
  • you can call class int(int), but you'd need to instantiate variable like: v= int(5) – SilentGhost Feb 05 '09 at 01:10
  • Thanks. So in effect this class can be written several times with different methods and int will get all of these? – Joan Venge Feb 05 '09 at 17:15
  • If you make multiple definitions like this, each one will inherit from the previous one, and so additional methods will accumulate in the most recently-defined "int" class. But the classes defined along the way don't change (tho you lose your reference to them), nor do previously-created instances. – Carl Meyer Feb 05 '09 at 18:02
  • 7
    NB: this is almost always a bad thing to do in real Python code. Unlike with extension methods in C#, here you are actually making the variable "int" refer to a completely different (inherited) class, which violates the principle of least surprise (i.e. suddenly isinstance(5, int) is False - WTF?) – Carl Meyer Feb 05 '09 at 18:04
  • @CarlMeyer Isn't this more like a reason not to use `isinstance`? – jwg Dec 30 '15 at 22:29
4

The following context manager adds the method like Forbidden Fruit would without the limitations of it. Besides that it has the additional benefit of removing the extension method afterwards:

class extension_method:

    def __init__(self, obj, method):
        method_name = method.__name__
        setattr(obj, method_name, method)
        self.obj = obj
        self.method_name = method_name

    def __enter__(self):
        return self.obj

    def __exit__(self, type, value, traceback):
        # remove this if you want to keep the extension method after context exit
        delattr(self.obj, self.method_name)

Usage is as follows:

class C:
    pass

def get_class_name(self):
    return self.__class__.__name__

with extension_method(C, get_class_name):
    assert hasattr(C, 'get_class_name') # the method is added to C
    c = C()
    print(c.get_class_name()) # prints 'C'

assert not hasattr(C, 'get_class_name') # the method is gone from C
mrts
  • 16,697
  • 8
  • 89
  • 72
2

I've had great luck with the method described here:

http://mail.python.org/pipermail/python-dev/2008-January/076194.html

I have no idea if it works on builtins though.

hernan43
  • 805
  • 1
  • 8
  • 18
-1

Another option is to override the meta-class. This allows you to, among other things, specify functions that should exist in all classes.

This article starts to discuss it:

http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html

tsellon
  • 2,396
  • 5
  • 24
  • 33