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... 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