I'm sorry Soni, I don't understand what you are arguing here. See below.

On Mon, Jun 21, 2021 at 10:09:17PM -0300, Soni L. wrote:
> 
> 
> On 2021-06-21 9:39 p.m., Steven D'Aprano wrote:
> 
> 
> >
> > Fourth step is that you go ahead and use lists as normal. Whether you 
> > use getattr or dot syntax, any extension methods defined in spam.py will 
> > show up, as if they were actual list methods.
> >
> >     hasattr([], 'head')  # returns True
> >     list.tail  # returns the spam.tail function object (unbound method)
> >
> > They're not monkey-patched: other modules don't see that.
> >
> >
> 
> Python is a dynamic language. Maybe you're using hasattr/getattr to
> forward something from A to B. If "other modules don't see that" then
> this must work as if there were no extension methods in place.

What's "forward something from A to B" mean? What are A and B?

If "this" (method lookups) "must work as if there were no extension 
methods in place" then extension methods are a no-op and are pointless. 
You write an extension method, register it as applying to a type, the 
caller opts-in to use it, and then... nothing happens, because it "must 
work as if there were no extension methods in place".

Surely that isn't what you actually want to happen. But if not, I have 
no idea what you mean.

The whole point of extension methods is that once the caller opts in to 
use them, method look ups (and that includes hasattr and getattr) must 
work as if the extension methods **are in place**.

The must be no semantic difference between:

    obj.method(arg)

and

    getattr(obj, 'method')(arg)

regardless of whether `method` is a regular method or an extension 
method.


> So you
> actually wouldn't want the local load_attr override to apply to those.
> If you did... well, just call the override directly.

I have no idea what that means. What is "the local load_attr override"?


> If the override was
> called __opcode_load_attr_impl__ you'd just call
> __opcode_load_attr_impl__ directly instead of going through getattr.

As a general rule, you should not be calling dunders directly.

You seem to have missed the point that extension methods are intended as 
a mechanism to **extend a type** by giving it new methods on an opt-in 
basis. I want to call them "virtual methods" except that would add 
confusion regarding virtual subclasses and ABCs etc.

Maybe you need to read the Kotlin docs:

https://kotlinlang.org/docs/extensions.html

and the C# docs:

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods

Wikipedia also has a broad overview from a language-agnostic 
perspective:

https://en.wikipedia.org/wiki/Extension_method

Here's an example in TypeScript and Javascript:

https://putridparrot.com/blog/extension-methods-in-typescript/



In particular note these comments:

# Kotlin
"Such functions are available for calling in the usual way as if they 
were methods of the original class."

# C#
"Extension methods are only in scope when you explicitly import the 
namespace into your source code with a using directive."

Both C# and Kotlin are statically typed languages, and Python is not, 
but we ought to aim to minimise the differences in semantics. Aside from 
extension methods being resolved at runtime instead of at compile time, 
the behaviour ought to be as close as possible.

Just as single dispatch in Python is resolved dynamically, but aims to 
behave as close as possible to single dispatch in statically typed 
languages.

Another important quote:

"Because extension methods are called by using instance method syntax, 
no special knowledge is required to use them from client code. To enable 
extension methods for a particular type, just add a `using` directive 
for the namespace in which the methods are defined."


"No special knowledge is required" implies that, aside from the opt-in 
step itself, extension methods must behave precisely the same as regular 
methods. That means they will be accessible as bound methods on the 
instance:

    obj.method

and unbound methods (functions) on the type:

    type(obj).method

and using dynamic lookup:

    getattr(obj, 'method')

and they will fully participate in inheritance heirarchies if you have 
opted in to use them.



> There needs to be an escape hatch for this.

The escape hatch is to *not* opt-in to the extension method. If the 
caller doesn't opt-in, they don't get the extension methods.

That is the critical difference between extension methods and monkey- 
patching the type. Monkey-patching effects everyone. Extension methods 
have to be opt-in.


> Or you *could* have getattr be special (called by load_attr) and
> overridable, and builtins.getattr be the escape hatch, but nobody would
> like that.

Huh? Unless you have shadowed getattr with a module-level function, 
getattr *is* builtins.getattr.


-- 
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/IR6Q5ENBEXFYCIEHLCDR5XJB3FZ77CYO/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to