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:

    x

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:

    obj.x

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.



-- 
Steve
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/C7FOXDQWES3X5K4F2OWCFBTWV5DPL5JQ/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to