15

In many discussions I have heard about Ruby in which people have expressed their reservations about the language, the issue of monkey patching comes up as one of their primary concerns.

However, I rarely hear the same arguments made in the context of Python although it is also permitted in the Python language.

Why this distinction?

Does Python include different types of safeguards to minimize the risks of this feature?

Paul Dexter
  • 405
  • 1
  • 4
  • 10
  • 1
    It's more common in Javascript than Python, probably just as common as in Ruby, if not more so – Dexygen Apr 05 '09 at 12:32

8 Answers8

21

It's a technique less practised in Python, in part because "core" classes in Python (those implemented in C) are not really modifiable. In Ruby, on the other hand, because of the way it's implemented internally (not better, just different) just about anything can be modified dynamically.

Philosophically, it's something that tends to be frowned on within the Python community, distinctly less so in the Ruby world. I don't know why you assert that it's more controversial (can you link to an authoritative reference?) - my experience has been that monkey-patching is an accepted technique if one where the user should be aware of possible consequences.

Mike Woodhouse
  • 51,832
  • 12
  • 88
  • 127
  • I can't speak about the Ruby community so I'll take your word that monkeypatching is accepted there, but if you walk into a room full of Python developers and ask them what they think of Ruby, you are more likely than not to hear the term "monkey patching" come up, and not in a positive light. – Paul Dexter Apr 04 '09 at 19:19
  • Yes, but 'controversial among Python programmers' is different than 'controverial among Rub programmers'. While it's possible to do monkey-patching in Python (and recognized as sometimes useful but dangerous), there are a number of language features of Ruby that seem to actively encourage it. – Jeff Shannon Apr 05 '09 at 01:21
  • (argh, that should be 'controversial among Ruby programmers' above, of course...) – Jeff Shannon Apr 05 '09 at 01:22
  • @Paul Dexter: I just see that difference as a consequence of the languages being different and some subset of the practitioners of both being ignorant of the strengths and weaknesses of the other. I use and like both languages but prefer Ruby, in part because of its (to me) broader OO capabilities. – Mike Woodhouse Apr 05 '09 at 08:56
16

The languages might permit it, but neither community condones the practice. Monkeypatching isn't condoned in either language, but you hear about it more often in Ruby because the form of open class it uses makes it very, very easy to monkeypatch a class and because of this, it's more acceptable in the Ruby community, but still frowned upon. Monkeypatching simply isn't as prevalent or as easy in Python, which is why you won't hear the same arguments against it in that community. Python does nothing that Ruby doesn't do to prevent the practice.

The reason you hear/read about it more often in Ruby is that this in Ruby:

class MyClass
  def foo
    puts "foo"
  end
end
class MyClass
  def bar
    puts "bar"
  end
end

will give you a class that contains two methods, foo and bar, whereas this in Python:

class MyClass:
    def foo(self):
        print "foo"
class MyClass:
    def bar(self):
        print "bar"

will leave you with a class that only contains the method bar, as redefinition of a class clobbers the previous definition completely. To monkeypatch in Python, you actually have to write this:

class MyClass:
    def foo(self):
        print "foo"
def bar(self):
    print "bar"
MyClass.bar = bar

which is harder than the Ruby version. That alone makes Ruby code much easier to monkeypatch than Python code.

Rory O'Kane
  • 29,210
  • 11
  • 96
  • 131
Keith Gaughan
  • 21,367
  • 3
  • 32
  • 30
  • Your Python example is valid, too. The second definition just creates a new MyClass-class and does not extend the previous one (I guess thats what ruby does). –  Apr 04 '09 at 18:20
  • You should add a note that it's technically valid – hasen Apr 05 '09 at 08:54
  • Can you provide some reference that shows the Ruby "community" not condoning the practice? What about ActiveSupport::CoreExtensions? – Mike Woodhouse Apr 05 '09 at 08:58
  • @hasen j: I think my latest edits should be enough. @Mike Woodhouse: Have done. I'm not saying there wasn't a laissez-faire attitude towards it in the past, but there's been a seachange towards its restrained use in the past year or two. Anyway, is Rails a fair example of Ruby? Its code's a mess! – Keith Gaughan Apr 05 '09 at 12:27
  • 4
    You can easily get the same effect in Python. Remove the second "class MyClass" line and put "MyClass.bar = bar" at the end after bar is defined. Also, you forgot the self argument for your class functions. – Lara Dougan Apr 07 '09 at 15:21
  • @Adam: No, you see, that's misses the point of what I'm demonstrating. Dropping the second class declaration would leave you with a single class declaration; a Python class declaration creates a new class object, where as Ruby ones extends existing ones. Also, self wasn't needed for the example. – Keith Gaughan Apr 07 '09 at 20:50
  • -1 per these comments. You always need self unless this is a static method. And Adam K. Johnson is right, the only thing you're demonstrating is that class declaration syntax does not destroy previously declared classes. But you absolutely can monkey patch, just using different syntax. Do you write Python regularly? – Jesse Dhillon Feb 01 '11 at 03:10
  • 1
    Yes, quite regularly as it happens - the lack of 'self' was just a typo. I'll edit it to fix that. What I was demonstrating is that Python does not have syntactically open classes as Ruby does. I never once said that you can't monkey patch in Python - that would be idiotic of me - just that Python classes are not syntactically open, and that lack of *syntactic* openness in Python classes is a barrier to monkeypatching that doesn't exist in Ruby. – Keith Gaughan Feb 01 '11 at 17:34
  • 1
    It doesn't seem from your answer that the link between the open/continuing declaration style of Ruby contributes to acceptance of monkey patching. I read this answer as being misinformed about what monkey patching is, particularly I thought you were saying the Ruby continuing class declaration syntax was the only way to monkey patch. +1 – Jesse Dhillon Feb 11 '11 at 17:50
16

As a Python programmer who has had a taste of Ruby (and likes it), I think there is somewhat of an ironic parallel to when Python was beginning to become popular.

C and Java programmers would ‘bash’ Python, stating that it wasn't a real language, and that the dynamic nature of its types would be dangerous, and allow people to create ‘bad’ code. As Python became more popular, and the advantages of its rapid development time became apparent, not to mention the less verbose syntax:

// Java
Person p = new Person();
# Python
p = Person()

we began to see some more dynamic features appear in later versions of Java. Autoboxing and -unboxing make it less troublesome to deal with primitives, and Generics allow us to code once and apply it to many types.

It was with some amusement that I saw one of the key flexible features of Ruby – Monkey Patching, being touted as dangerous by the Python crowd. Having started teaching Ruby to students this year, I think that being able to ‘fix’ the implementation of an existing class, even one that is part of the system, is very powerful.

Sure, you can screw up badly and your program can crash. I can segfault in C pretty easily, too. And Java apps can die flaming death.

The truth is, I see Monkey Patching as the next step in dynamic and meta-programming. Funny, since it has been around since Smalltalk.

pmr
  • 58,701
  • 10
  • 113
  • 156
Matthew Schinckel
  • 35,041
  • 6
  • 86
  • 121
  • 3
    I have heard the term "duck-punching" used in the same vein as "monkey-patching". It is a bit of a play on "duck typing" (if it quacks like a duck, then it may as well be a duck, even if it is an integer). If it doesn't quack like a duck, then you punch it until it does (add new methods, etc). – Matthew Schinckel Jul 28 '10 at 08:27
13

"Does Python include different types of safeguards to minimize the risks of this feature?"

Yes. The community refuses to do it. The safeguard is entirely social.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
3

Actually in Python it's a bit harder to modify basic types.

For example imagine, that you redefine integer.

Ruby:

class Fixnum 
   def *(n)
      5 
   end 
end

Now 2*2 yields 5.

Python:

>>> class int(int):
    def __mul__(self, x):
        return 5


>>> 2*2
4
>>> int(2)*int(2)
5
vartec
  • 131,205
  • 36
  • 218
  • 244
  • That Ruby example doesn't do what you think. That would make it so you'd do Integer.times {|five| "The number is one less than #{five + 1}"} and it would return 6. 2*2, however, would still be 4. You want class Fixnum; def *(n); 5; end; end . – Chuck Apr 05 '09 at 03:07
  • @Chuck: thanks, I really haven't got much experience with Ruby (yet), I'm a Python guy ;-) – vartec Apr 05 '09 at 08:58
3

In Python, any literal ("", {}, 1.0, etc) creates an instance of the standard class, even if you tried to monkeypatch it and redefined the corresponding class in your namespace.

It just won't work how you intended:

class str():
    # define your custom string type
    ...

a = "foo"      # still a real Python string
a = str("foo") # only this uses your custom class
Rory O'Kane
  • 29,210
  • 11
  • 96
  • 131
Algorias
  • 3,043
  • 5
  • 22
  • 16
  • Ruby works the same way, so that’s ***not*** a difference. If I `def String.new(*args, &block); Array.new; end` in Ruby, then although `String.new("foo")` results in `[]`, `"foo"` still results in `"foo"`. – Rory O'Kane Jul 20 '12 at 16:44
  • Consider this bit of ruby: – John Allsup Jan 07 '17 at 21:53
2

I think that monkey patching should only be used as the last solution.

Normally Python programmers know how a class or a method behave. They know that class xxx is doing things in a certain way.

When you monkey patch a class or a method, you are changing it's behavior. Other Python programmers using this class can be very surprised if that class is behaving differently.

The normal way of doing things is subclassing. That way, other programmers know that they are using a different object. They can use the original class or the subclass if they choose to.

Oli
  • 15,345
  • 8
  • 30
  • 36
  • What if there are no other Python programmers using a script? Or the script is short? The kind of 'dangerous' arguments we hear are very valid when dealing with a 50+kLOC project. For small scripts, however, not being able to money patch can mean tens to hundreds of lines of boilerplate for a script that would otherwise be a few lines long. I would love a 'sloppy python' for small scripts (where any competent programmer can read and remember the whole thing in a few minutes). Most software engineering disciplines are for large projects. – John Allsup Jan 07 '17 at 22:04
1

If you want to do some monkey patching in Python, it is relatively easy, as long as you are not modifying a built-in type (int, float, str).

class SomeClass:
    def foo(self):
        print "foo"

def tempfunc(self):
    print "bar"
SomeClass.bar = tempfunc
del tempfunc

This will add the bar method to SomeClass and even existing instances of that class can use that injected method.

Lara Dougan
  • 791
  • 4
  • 13
  • Also not-monkey-patchable: datetime.datetime, datetime.timedelta, and many, many more. Shame, as there are ways I want to extend these classes often. – Matthew Schinckel Jul 28 '10 at 08:25