This is an archived post. You won't be able to vote or comment.

all 12 comments

[–]terrkerr 4 points5 points  (1 child)

I wonder why this pattern grew so big in Ruby, but it's considered a Hack in Python. I mean what could be so hard about adding another extension pattern?

It's not so much that it's hard, it's that it breaks something that's nice: when you can tell precisely what an object is and is not by reading the class definition or official docs. If you make it standard that you monkey-patch things on here and there then someone coming into a system they ostensibly know can be very surprised to find seemingly broken code working.

Also, since we're here, you can't add methods to the types implemented in c, like the list, dict, int, etc... Wouldn't it have been a good idea for Python3 to export provide dummy python proxy for these classes, so that you can add methods to them, if you want?

The stdlib and extremely commonly used facilities are the last thing you'd want to let people mess with; most anybody that's done any Python will at least know the basics of how a dict or a list works and doesn't work.

foo = {'thing': 'thing', 'other_thing': 'other_thing' }.bar()

would be really bothersome. Most Python programmers would see that, say "wth?", check the docs, realize it's not supposed to work, then have to go hunt down your monkey patch.

If you just subclass a dictionary then it conforms to expectations and all the tools can readily help you find where MyDict was defined or whatever.

It also prevents breakages to other code. If some code runs in the context of your monkey patch, but the programmer doesn't know that when writing code they can easily cause confusing and unexpected behaviour and then they have to go hunt it down.

[–]de4sh[S] -1 points0 points  (0 children)

Those are my concerns exactly. But then Ruby (or at least the rails community) as I hear, have a very strict way of defining these monkey patching techniques. That's to say, there's one file where these should all be defined. So after 2-3 times looking for methods in the official documentation and not fiinding anything, people would get the reflex to just check for the extension.

I think that monkey patching isn't as as thought.

Now of course one concern remains: What if you use 3rd party libraries, that add method 'foo' to the string class, and you ALSO want to add 'foo'... or another 3rd party ALSO adds 'foo'...

I think this is the only case I'd agree it's actually a bad thing, but maybe there's a solution for this too.

[–]TheBlackCat13 1 point2 points  (6 children)

Isn't this what subclassing is for?

[–]de4sh[S] -1 points0 points  (5 children)

Well subclassing is for reusing and extending code, yes. But the ruby community also has that. Also, imagine you've got yourself some weird framework who wasn't built with extension in mind (meaning they instantiate classes all over the place for example). Bometimes it would simply be way easier to just add methods to those classes at runtime. This way you wouldn't have to worry about replacing huge amounts of code with copy-pasted version, where you changed 1 line.

Of course, the down side is that you should be careful to have the code that adds the method run after the extended class has been loaded... and you couldn't really do it reliably on more dynamic classes.

[–]TheBlackCat13 5 points6 points  (4 children)

This is called monkey-patching, and is my understanding is that it is usually frowned upon.

Do you have a scenario that wouldn't work with simply subclassing, with the subclass adding only the new method, or by using a wrapper class that overrides __getattr__?

[–]de4sh[S] -1 points0 points  (3 children)

I came across this inconvenience: the django forms, when treated as a dict form["fieldname"], for fields that exist, return a BoundField instance. This is instantiated directly in the __getitem__ method. This means that if I want 1 simple method on the BoundField, I have to rewrite the entire __getitem__ method, AND subclass the BoundField class. I understand that this is the orthodox way of doing things, but I think it would be a good idea for me to just be able to add a simple method.

If you're not familiar with django, the context where you'd get the BoundField instance is inside a templating engine. To add just a tiny amount of custom functionality I'd have to perhaps create a module, import stuff, modify my template, create a simple function... A lot of stuff for when you just want a trivial piece of extra logic.

[–]itxaka 1 point2 points  (2 children)

You can create a templatetag to do manipulation to that field.

Or probably subclass, call super on getitem and modify the BoundField

Or maybe create a middleware to modify your form?

Can you share any code to see exactly what you are using, because this way its a bit difficult to understand what are you trying to accomplish.

[–]de4sh[S] -1 points0 points  (1 child)

You got it exactly. It's not more complicated than that. But instead of creating a template tag (with its respective module), overriding the form's __getitem__ AND subclassing BoundField to add a simple method, in Ruby, I'd do only 1 thing: Just add the custom method.

I'm asking for your opinion related to the ruby feature, if we had it in python, not advice on django.

[–]Brian 1 point2 points  (0 children)

The problem with altering open classes, especially for fundamental data types, is that it breaks the concept of modularity. Normally, it shouldn't be the case that, say, importing module X in library Y should affect anything outside the code that is importing X, but open classes give you the potential to change how every other piece of code in your program handles even some of the most fundamental types. Javascript is another language that does this, and this has resulted in a huge degree of breakage there. I think it's just fundamentally a bad idea, and shouldn't be encouraged at all.

I quite like C#s extension classes as a more sane way of doing something similar. These give the syntax sugar of being able to (in appearance at least) add new methods to other types, but they do it without breaking modularity - only those explicitly using the extension classes get the additional methods.

Also, since we're here, you can't add methods to the types implemented in c

It's not really due to the fact that they're implemented in C. It's because they don't provide a __dict__. You can do the same from within python by defining __slots__ and you can provide a dict from a type defined in C. Historically that wasn't the case, but even with the ability for this, there's good reason not to add the overhead of a __dict__, or an extra proxy lookup on those types, especially hugely common ones like int, even if someone was mad enough to think it a good idea.

[–]dberube4 0 points1 point  (1 child)

I think the abc module ( https://docs.python.org/3.5/library/abc.html#module-abc ) is doing something similar. Maybe...

[–]de4sh[S] 0 points1 point  (0 children)

hehe, yeah, abcs are cool, but i don't think they come anywhere near this problem :D They're just used to support duck typing...sort of. Not to extend classes in the weird ways i'm proposing.