57

Ruby can add methods to the Number class and other core types to get effects like this:

1.should_equal(1)

But it seems like Python cannot do this. Is this true? And if so, why? Does it have something to do with the fact that type can't be modified?

Rather than talking about different definitions of monkey patching, I would like to just focus on the example above. I have already concluded that it cannot be done as a few of you have answered. But I would like a more detailed explanation of why it cannot be done, and maybe what feature, if available in Python, would allow this.

To answer some of you: The reason I might want to do this is simply aesthetics/readability.

 item.price.should_equal(19.99)

This reads more like English and clearly indicates which is the tested value and which is the expected value, as supposed to:

should_equal(item.price, 19.99)

This concept is what Rspec and some other Ruby frameworks are based on.

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
airportyh
  • 21,948
  • 13
  • 58
  • 72
  • 4
    well, to get the item.price.should_equal(19.99) working just make price a subclass of numeric type, and add the method should_equal for it. should work. – kender Oct 10 '08 at 21:16
  • 1
    Bla bla bla, yes you can do that in python, that's exactly what ["sure"](https://github.com/gabrielfalcao/sure#fluent-assertions) does. [https://github.com/gabrielfalcao/sure#fluent-assertions](https://github.com/gabrielfalcao/sure#fluent-assertions) – Gabriel Falcão Aug 08 '12 at 07:19
  • See also: http://stackoverflow.com/questions/352537/extending-builtin-classes-in-python – dreftymac Sep 04 '13 at 04:47

15 Answers15

81

No, you cannot. In Python, all data (classes, methods, functions, etc) defined in C extension modules (including builtins) are immutable. This is because C modules are shared between multiple interpreters in the same process, so monkeypatching them would also affect unrelated interpreters in the same process. (Multiple interpreters in the same process are possible through the C API, and there has been some effort towards making them usable at Python level.)

However, classes defined in Python code may be monkeypatched because they are local to that interpreter.

user2357112
  • 260,549
  • 28
  • 431
  • 505
John Millikin
  • 197,344
  • 39
  • 212
  • 226
  • @AlexanderLjungberg the place to suggest changes to Python is https://bugs.python.org (or better yet, working code submitted to their git repository), not stackoverflow. – Boris Verkhovskiy Apr 29 '19 at 11:57
  • 1
    This answer is incomplete nowadays without mentioning [`forbiddenfruit`](https://pypi.org/project/forbiddenfruit). – mtraceur Jul 30 '21 at 00:39
  • You say that `builtins` are immutable, but ins't `list` a mutable type and a builtin? – astralwolf Sep 02 '21 at 15:02
  • @astralwolf `list` is immutable. `list`s are mutable. There is a distinction between the class and its instances. – Grant Gryczan May 25 '22 at 09:05
46

What exactly do you mean by Monkey Patch here? There are several slightly different definitions.

If you mean, "can you change a class's methods at runtime?", then the answer is emphatically yes:

class Foo:
  pass # dummy class

Foo.bar = lambda self: 42

x = Foo()
print x.bar()

If you mean, "can you change a class's methods at runtime and make all of the instances of that class change after-the-fact?" then the answer is yes as well. Just change the order slightly:

class Foo:
  pass # dummy class

x = Foo()

Foo.bar = lambda self: 42

print x.bar()

But you can't do this for certain built-in classes, like int or float. These classes' methods are implemented in C and there are certain abstractions sacrificed in order to make the implementation easier and more efficient.

I'm not really clear on why you would want to alter the behavior of the built-in numeric classes anyway. If you need to alter their behavior, subclass them!!

Dan Lenski
  • 76,929
  • 13
  • 76
  • 124
32

You can do this, but it takes a little bit of hacking. Fortunately, there's a module now called "Forbidden Fruit" that gives you the power to patch methods of built-in types very simply. You can find it at

http://clarete.github.io/forbiddenfruit/?goback=.gde_50788_member_228887816

or

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

With the original question example, after you write the "should_equal" function, you'd just do

from forbiddenfruit import curse
curse(int, "should_equal", should_equal)

and you're good to go! There's also a "reverse" function to remove a patched method.

alcalde
  • 1,288
  • 13
  • 10
  • 1
    Seems weird. Some stuff I try works just fine. Other stuff I try seems to just get ignored. Can't find any pattern, rhyme, or reason for it. – ArtOfWarfare Jul 03 '15 at 22:25
  • very cool! it even works with list (which many solutions do not) letting you chain operations like javascript/scala in Python – kmader Jun 24 '19 at 12:29
31
def should_equal_def(self, value):
    if self != value:
        raise ValueError, "%r should equal %r" % (self, value)

class MyPatchedInt(int):
    should_equal=should_equal_def

class MyPatchedStr(str):
    should_equal=should_equal_def

import __builtin__
__builtin__.str = MyPatchedStr
__builtin__.int = MyPatchedInt

int(1).should_equal(1)
str("44").should_equal("44")

Have fun ;)

Jonathan
  • 335
  • 3
  • 2
  • 9
    Its very wrong because it makes programmers on other projects want you DEAD. Imagine I import your big ass library and you monkeypatch int. Now what happens if int doesn't behave like I expect it to? – Jakob Bowyer Oct 21 '11 at 14:20
  • 3
    @JakobBowyer I think it's very wrong only when you use `from HisPatch import *` – Hanfei Sun Feb 19 '13 at 11:44
  • 3
    @Firegun The problem is, _any_ import from `HisPatch` would cause that code to be executed and _globally_ modify the builtin classes. [Not that I didn't consider using something like this...](http://stackoverflow.com/a/16058636/321973) – Tobias Kienzler Dec 08 '14 at 13:05
  • 2
    This doesn't appear to work for all cases. Try replacing __builtin__.list, for instance, and then look at the object you get from range(4). It is of the same old built in Pythin list type. – holbech Dec 04 '15 at 08:49
14

Python's core types are immutable by design, as other users have pointed out:

>>> int.frobnicate = lambda self: whatever()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'int'

You certainly could achieve the effect you describe by making a subclass, since user-defined types in Python are mutable by default.

>>> class MyInt(int):
...   def frobnicate(self):
...     print 'frobnicating %r' % self
... 
>>> five = MyInt(5)
>>> five.frobnicate()
frobnicating 5
>>> five + 8
13

There's no need to make the MyInt subclass public, either; one could just as well define it inline directly in the function or method that constructs the instance.

There are certainly a few situations where Python programmers who are fluent in the idiom consider this sort of subclassing the right thing to do. For instance, os.stat() returns a tuple subclass that adds named members, precisely in order to address the sort of readability concern you refer to in your example.

>>> import os
>>> st = os.stat('.')
>>> st
(16877, 34996226, 65024L, 69, 1000, 1000, 4096, 1223697425, 1223699268, 1223699268)
>>> st[6]
4096
>>> st.st_size
4096

That said, in the specific example you give, I don't believe that subclassing float in item.price (or elsewhere) would be very likely to be considered the Pythonic thing to do. I can easily imagine somebody deciding to add a price_should_equal() method to item if that were the primary use case; if one were looking for something more general, perhaps it might make more sense to use named arguments to make the intended meaning clearer, as in

should_equal(observed=item.price, expected=19.99)

or something along those lines. It's a bit verbose, but no doubt it could be improved upon. A possible advantage to such an approach over Ruby-style monkey-patching is that should_equal() could easily perform its comparison on any type, not just int or float. But perhaps I'm getting too caught up in the details of the particular example that you happened to provide.

zaphod
  • 4,561
  • 3
  • 25
  • 26
8

You can't patch core types in python. However, you could use pipe to write a more human readable code:

from pipe import *

@Pipe
def should_equal(obj, val):
    if obj==val: return True
    return False

class dummy: pass
item=dummy()
item.value=19.99

print item.value | should_equal(19.99)
Lvsoft
  • 81
  • 1
  • 1
  • Nice, thanks! Here's the Pipe project page on PyPI: http://pypi.python.org/pypi/pipe – michel-slm May 31 '12 at 10:03
  • 1
    Nice addition, although this is not within core python, so that has drawbacks for people who are not authorized to import custom libraries – dreftymac Sep 04 '13 at 04:42
4

If you really really really want to do a monkey patch in Python, you can do a (sortof) hack with the "import foo as bar" technique.

If you have a class such as TelnetConnection, and you want to extend it, subclass it in a separate file and call it something like TelnetConnectionExtended.

Then, at the top of your code, where you would normally say:

import TelnetConnection

change that to be:

import TelnetConnectionExtended as TelnetConnection

and then everywhere in your code that you reference TelnetConnection will actually be referencing TelnetConnectionExtended.

Sadly, this assumes that you have access to that class, and the "as" only operates within that particular file (it's not a global-rename), but I've found it to be useful from time to time.

HanClinto
  • 9,423
  • 3
  • 30
  • 31
4

Here's an example of implementing item.price.should_equal, although I'd use Decimal instead of float in a real program:

class Price(float):
    def __init__(self, val=None):
        float.__init__(self)
        if val is not None:
            self = val

    def should_equal(self, val):
        assert self == val, (self, val)

class Item(object):
    def __init__(self, name, price=None):
        self.name = name
        self.price = Price(price)

item = Item("spam", 3.99)
item.price.should_equal(3.99)
Ryan Ginstrom
  • 13,915
  • 5
  • 45
  • 60
1

No but you have UserDict UserString and UserList which were made with exactly this in mind.

If you google you will find examples for other types, but this are builtin.

In general monkey patching is less used in Python than in Ruby.

Luka Marinko
  • 1,693
  • 2
  • 11
  • 14
1

It seems what you really wanted to write is:

assert item.price == 19.99

(Of course comparing floats for equality, or using floats for prices, is a bad idea, so you'd write assert item.price == Decimal(19.99) or whatever numeric class you were using for the price.)

You could also use a testing framework like py.test to get more info on failing asserts in your tests.

Petr Viktorin
  • 65,510
  • 9
  • 81
  • 81
1

What does should_equal do? Is it a boolean returning True or False? In that case, it's spelled:

item.price == 19.99

There's no accounting for taste, but no regular python developer would say that's less readable than your version.

Does should_equal instead set some sort of validator? (why would a validator be limited to one value? Why not just set the value and not update it after that?) If you want a validator, this could never work anyway, since you're proposing to modify either a particular integer or all integers. (A validator that requires 18.99 to equal 19.99 will always fail.) Instead, you could spell it like this:

item.price_should_equal(19.99)

or this:

item.should_equal('price', 19.99)

and define appropriate methods on item's class or superclasses.

0

No, sadly you cannot extend types implemented in C at runtime.

You can subclass int, although it is non-trivial, you may have to override __new__.

You also have a syntax issue:

1.somemethod()  # invalid

However

(1).__eq__(1)  # valid
Dima Tisnek
  • 11,241
  • 4
  • 68
  • 120
0

No, you can't do that in Python. I consider it to be a good thing.

sanxiyn
  • 3,648
  • 1
  • 19
  • 15
0

Here is how I made custom string/int/float...etc. methods:

class MyStrClass(str):

    def __init__(self, arg: str):
        self.arg_one = arg

    def my_str_method(self):
        return self.arg_one

    def my_str_multiple_arg_method(self, arg_two):
        return self.arg_one + arg_two

class MyIntClass(int):

    def __init__(self, arg: int):
        self.arg_one = arg

    def my_int_method(self):
        return self.arg_one * 2


myString = MyStrClass("StackOverflow")
myInteger = MyIntClass(15)

print(myString.count("a"))  # Output: 1
print(myString.my_str_method())  # Output: StackOverflow
print(myString.my_str_multiple_arg_method(" is cool!"))  # Output: StackOverflow is cool!
print(myInteger.my_int_method())  # Output: 30

It's maybe not the best solution, but it works just fine.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
electroJo
  • 11
  • 1
-1

Here's how I achieve the .should_something... behavior:

result = calculate_result('blah') # some method defined somewhere else

the(result).should.equal(42)

or

the(result).should_NOT.equal(41)

I included a decorator method for extending this behavior at runtime on a stand-alone method:

@should_expectation
def be_42(self)
    self._assert(
        action=lambda: self._value == 42,
        report=lambda: "'{0}' should equal '5'.".format(self._value)
    )

result = 42

the(result).should.be_42()

You have to know a bit about the internals but it works.

Here's the source:

https://github.com/mdwhatcott/pyspecs

It's also on PyPI under pyspecs.

guneysus
  • 6,203
  • 2
  • 45
  • 47
Michael Whatcott
  • 5,603
  • 6
  • 36
  • 50