Can you use monkey patch methods for basic types in python?

Ruby can add methods to the Number class and other basic types to get effects such as:

 1.should_equal (1)

But it looks like python cannot do this. It's true? And if so, why? Does this have to do with the fact that the type cannot be changed?

Update: Instead of talking about the various definitions of monkey patches, I would just like to focus on the above example. I have already concluded that this cannot be done, as some of you answered. But I would like to get a more detailed explanation of why this is impossible to do, and maybe what function, if available in python, will allow this.

To answer some of you: the reason I can do this is simply aesthetics / readability.

 item.price.should_equal (19.99)

reads more like in English and clearly indicates what is the tested value and which is the expected value, as expected:

 should_equal (item.price, 19.99)

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

+43
python ruby programming-languages monkeypatching fluent-interface
Oct 10 '08 at 18:56
source share
14 answers

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

If you mean, “can you change the class methods at runtime?” Then the answer will be decidedly yes:

class Foo: pass # dummy class Foo.bar = lambda self: 42 x = Foo() print x.bar() 

If you mean: "Can you change the class methods at runtime and make all instances of this class changed after ?" then the answer is yes. Just slightly reorder:

 class Foo: pass # dummy class x = Foo() Foo.bar = lambda self: 42 print x.bar() 

But you cannot do this for certain built-in classes like int or float . These class methods are implemented in C, and there are some sacrificed abstractions to make the implementation simpler and more efficient.

I don’t quite understand why why you want to change the behavior of built-in number classes. If you need to change their behavior, subclass them!

+36
Oct 10 '08 at 19:15
source share

No, you can’t. In Python, all data (classes, methods, functions, etc.) defined in C extension modules (including built-in) is immutable. This is because C-modules are shared by several interpreters in the same process, so monkeypatching them also affects unrelated interpreters in the same process.

However, classes defined in Python code can be rendered harmless because they are local to this interpreter.

+61
Oct 10 '08 at 20:01
source share
 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") 

Good luck;)

+25
Oct 26 2018-10-26
source share

You can do this, but it requires a bit of hacking. Fortunately, there is now a module called "Forbidden Fruit" that allows you to fix methods of built-in types very simply. You can find it on

http://clarete.imtqy.com/forbiddenfruit/?goback=.gde_50788_member_228887816

or

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

With the original question question, after you write the "should_equal" function, you simply do

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

and you are good to go! There is also a function to “reverse” delete a fixed method.

+22
Jun 22 '13 at 0:33
source share

Python kernel types are immutable in design, as other users point 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' 

Of course, you could achieve the effect that you describe by creating a subclass, since custom types in Python change by default.

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

There is no need to publish a subclass of MyInt public; You can precisely define it directly in the function or method that creates the instance.

There are, of course, several situations where Python programmers who are fluent in the idiom believe that this subclass class is correct. For example, os.stat() returns a subclass of tuple that adds named members, just to address the issue of readability, which you are talking about 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 

However, in the specific example you are giving, I do not believe that subclassing float in item.price (or elsewhere) will be very likely to be considered a pythonic task. I can easily imagine that someone decided to add the price_should_equal() method to item , if that was the main use case; if someone were looking for something more general, it might be wiser to use named arguments to make the intended value more clear, as in

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

or something like that. This is a bit verbose, but no doubt it could be improved. A possible advantage of this approach over a ruby ​​monkey patch is that should_equal() can easily perform comparisons of any type, not just int or float . But maybe I'm too fixated on the details of the specific example that was provided to you.

+13
Oct 11 '08 at 4:50
source share

You cannot change basic types in python. However, you can use the pipe to write 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) 
+7
May 13 '11 at 6:35
source share

Here is an example implementation of item.price.should_equal , although I would 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) 
+4
May 08 '09 at 2:36
source share

If you really really want to make a monkey patch in Python, you can (sortof) hack using the "import foo as bar" method.

If you have a class like TelnetConnection, and you want to extend it, subclass it into a separate file and name it something like TelnetConnectionExtended.

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

 import TelnetConnection 

change this value:

 import TelnetConnectionExtended as TelnetConnection 

and then everywhere in your code that you reference TelnetConnection will actually refer to TelnetConnectionExtended.

Unfortunately, this assumes that you have access to this class, and the “how” only works in this particular file (this is not a global rename), but I found it useful from time to time.

+3
Oct 10 '08 at 19:09
source share

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

You can subclass int, although this is not trivial, you may need to override __new__ .

You also have a syntax issue:

 1.somemethod() # invalid 

However

 (1).__eq__(1) # valid 
+2
Dec 20 '12 at 11:19
source share

No, you cannot do this in Python. I think this is good.

+1
Oct 10 '08 at 18:57
source share

No, but you have a UserDict UserString and UserList that were made with this in mind.

If you are google, you will find examples for other types, but these are built-in functions.

In general, monkey patting is less used in Python than in Ruby.

+1
Oct 10 '08 at 19:13
source share

What does should_equal do? Is this a logical return of True or False ? In this case, it is written:

 item.price == 19.99 

There is no accounting for taste, but no ordinary python developer would say that it is less readable than your version.

Does should_equal any validator? (why should the validator be limited to one value? Why not just set the value and update it after that?) If you want a validator, it will never work anyway, since you are proposing to change either a specific integer or all integers numbers. (A validator that requires 18.99 equal to 19.99 always fails.) Instead, you can write it like this:

 item.price_should_equal(19.99) 

or that:

 item.should_equal('price', 19.99) 

and define appropriate methods for the class or superclasses.

+1
May 6 '09 at 3:15
source share

It seems that you really wanted to write:

 assert item.price == 19.99 

(Of course, comparing a float for equality or using a float for prices is a bad idea , so you write assert item.price == Decimal(19.99) or any digital class that you used for price.)

You can also use a testing platform like py.test to get more information about failed assertions in tests.

+1
Jun 05 2018-11-11T00:
source share

Here, how I achieve .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 have included a decorator method to extend this runtime behavior in an autonomous way:

 @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 should know a little about the insides, but it works.

Here's the source:

https://github.com/mdwhatcott/pyspecs

This is also on PyPI under pyspecs.

0
Jun 05 2018-12-12T00:
source share



All Articles