On Wed, Jun 23, 2021 at 11:22:26AM -0700, Brendan Barnwell wrote:
On 2021-06-23 03:02, Steven D'Aprano wrote:
Attribute lookups are just another form of name lookup. Name lookups depend on the current execution scope, not the caller's scope. With extension methods, so do attribute lookups.
But that's the thing, they aren't.
Of course attribute lookups are another form of name lookup. One hint that this is the case is that some languages, such as Java, call attributes *variables*, just like local and global variables. Both name and attribute lookups are looking up some named variable in some namespace. This shouldn't be controversial.
When you look up bare name:
the interpreter follows the LEGB rule and looks for `x` in the local scope, the enclosing scope, the global scope and then the builtin scope. There are some complications to do with locals and class scopes, and comprehensions, etc, but fundamentally you are searching namespaces for names. Think of the LEGB rule as analogous to an MRO.
When you look up a dotted name:
the interpreter looks for `x` in the instance scope, the class scope, and any superclass scopes. The detailed search rules are different, what with decorators, inheritance, etc, the MRO could be indefinitely long, and there are dynamic attributes (`__getattr__`) too.
But fundamentally, although the details are different, attribute access and name lookup are both looking up names in namespaces.
You gave a bunch of examples of lexical scope semantics with imports and function locals vs globals. But attribute lookups do not work that way. Attribute lookups are defined to work via a FUNCTION CALL to the __getattribute__ (and thence often the __getattr__) of the OBJECT whose attribute is being looked up.
There's that weird obsession with "function call" again. Why do you think that makes a difference?
They do not in any way depend on the name via which that object is accessed.
Ummmm... yes? Why is that relevant?
Now of course you can say that you want to make a new rule that throws the old rules out the window.
That's pure FUD. Extension methods don't require throwing the old rules out the window. The old rules continue to apply.
We can do that for anything. We can define a new rule that says now when you do attribute lookups it will call a global function called attribute_lookups_on_tuesdays if it's a Tuesday in your timezone.
If you want to do that then go ahead and propose it in another thread, but I don't want anything like that strawman.
But what I'm saying is that the way attribute lookups currently work is not the same as the way bare-name lookups work, because attribute lookups are localized to the object
You might have heard of something called "inheritance". And dynamic attributes. Maybe even mixins or traits.
Attribute lookups are not localised to the object.
(not the name!) and bare-name lookups are not. I consider this difference fundamental to Python.
There are plenty of differences between name lookups and attribute lookups, but locality is not one of them.
It's why locals() isn't really how local name lookups work (which came up elsewhere in this thread).
I don't see the relevance to extension methods.
It's why you can't magically hook into "x = my_obj" and create some magical behavior that depends on my_obj.
I don't see the relevance to extension methods. You seem to be just listing random facts as if they were objections to the extension method proposal.
You're right, we can't hook into assignment to a bare name. So what? We *can* hook into attribute lookups.
As for other languages, you keep referencing them as if the fact that something known as "extension methods" exists in those other languages makes it self-evident that it would be useful in Python. Python isn't those other languages. I'm not familiar with all of the other languages you mentioned, but I'll bet that at least some of them do not have the same name/attribute lookup rules and dunder-governed object-customization setup as Python. So that's the difference.
I think that's a difference that makes no difference.
What if I told you that it is likely that Python's name/attribute lookup rules and dunder-governed object-customization are the key features that would make extension methods possible with little or no interpreter support?
As I described in a previous post, adding extension methods would be a very small change to the existing attribute lookup rules. The tricky part is to come up with a fast, efficient registration system for determining when to use them.
The fact that extension methods happen to exist and be useful in those languages is really neither here nor there.
Extension methods don't just "happen to exist", they were designed to solve a real problem. That's a problem that can apply to Python just as much as other languages.
Your argument here sounds like Not Invented Here. "Sure they're useful, in *other* (lesser) languages, not in Python! We never have any need to extend classes with new methods -- apart from all the times we do, but they don't count."
The attribute lookup procedure you are proposing is deeply inconsistent with the way Python currently does attribute lookup
"Deeply inconsistent" in what way?
`__getattr__` can already do **literally anything** on attribute lookups? Anything that you can do in Python, you can do in a `__getattr__` method. Including adding arbitrary new methods.
and currently does other things (like operator overloading), and doesn't fit into Python's overall mechanism of object-based hooks. A spatula attachment may be useful on a kitchen mixer; that doesn't mean it's a good idea to add one to your car's dashboard.
Fortunately this is not a proposal to add spatulas to car dashboards. It is a proposal to add a mechanism to extend classes with extra functionality -- an extremely common activity in OOP.
I think this proposal would do the opposite, making code harder to read.
Okay. Here's a method call with a regular method:
obj.method(arg) # easy to read, understandable
Here's a method call with a extension method:
obj.method(arg) # unreadable, incomprehensible garbage
Yes, you're absolutely right, the second is *so much harder to read*.
Seriously, there's a time to realise when arguments against a feature devolve down to utterly spurious claims that Python programmers are idiots who will be confused by:
from extensions use flatten mylist.flatten()
but can instantly understand:
from extensions import flatten flatten(mylist)
If you can understand importing functions, you can understand using extension methods. If anything, extension methods are simpler: there's not likely to be all the complications of:
- circular imports, which are tricky even for seasoned Pythonistas;
- differences between standard import and import from, which often trips beginners up;
- absolute and relative imports;
- regular and namespace packages;
and other quirks of importing. Compared to all of those, extension methods are likely to be simple and straightforward.