On Tue, Jun 22, 2021 at 9:23 PM Steven D'Aprano firstname.lastname@example.org wrote:
On Tue, Jun 22, 2021 at 05:50:48PM +1000, Chris Angelico wrote:
Hmm, that's not what I'd usually understand "encapsulation" to mean. That's what would normally be called "namespacing".
Pfft, who you going to believe, me or some random folx on the internet editing Wikipedia? *wink*
Okay, using the Wikipedia/OOP definition still applies. Presumably most extension methods are going to be regular instance methods or class methods, rather than staticmethod. So they will take a `self` (or `cls`) parameter, and presumably most such extension methods will actually act on that self parameter in some way.
There is your "bundling of data with the methods that operate on that data", as required :-)
Okay, that's fair. Granted. It's not ALL of encapsulation, but it is, to an extent encapsulation. (It is also namespacing, and your justification of it was actually a justification of namespacing; but this is Python, and I think we all agree that namespacing is good!)
Regarding your example, you're only confused because you haven't take on board the fact that extension methods aren't interpreter-global, just module-global. Because it's new and unfamiliar. But we can do exactly the same thing, right now, with functions instead of methods, and you will find it trivially easy to diagnose the fault:
# file1.py from file2 import func # instead of "applying an extension method" from elsewhere, # import a function from elsewhere from extra_functions import g def spamify(obj): print(g(obj)) # works fine print(func(obj)) # fails # file2.py def func(obj): return g(obj) # NameError
This example looks easy and not the least bit scary to you because you've been using Python for a while and it has become second nature to you. But you might remember back when you were a n00b, it probably confused you: why doesn't `g(obj)` work when you imported it? How weird and confusing! What do you mean, if I want to use g, I have to import it in each and every module where I want to use it? That's just dumb. Importing g once should make it available EVERYWHERE, right?
Been there, done that.
Fair point. However, I've worked with a good number of languages that have some notion of object methods, and generally, an object has or doesn't have a method based on what the object *is*, not on who's asking. It's going to make for some extremely confusing results. Is getattr() going to act as part of the builtins module or the module that's calling it? What about hasattr? What about an ABC's instance check, or anything else?
How do other languages deal with this? How do they have a getattr-like function? Does it have to be a compiler construct?
Chris, here you are defending monkey-patching, not just as a necessary evil under some circumstances, but as a "cleaner" option, and then in your very next sentence:
And the Ruby community is starting to see the risks of monkey-patching.
Yes. That is exactly right. I am claiming that monkey-patching is, in many MANY cases, a cleaner option than extension methods. And then I am saying that monkey-patching is usually a bad thing. This is not incompatible, and it forms a strong view of my opinion of extension methods.