On Wed, Jun 23, 2021 at 07:23:19PM +0200, João Santos wrote:
> 
> 
> On Wed, Jun 23 2021 at 20:48:39 +1000, Steven D'Aprano 
> <st...@pearwood.info> wrote:
> >I've just thought of a great use-case for extension methods.
> >
> >Hands up who has to write code that runs under multiple versions of
> >Python? *raises my hand*
> >
> >I'm sure I'm not the only one. You probably have written compatibility
> >functions like this:
> >
> >    def bit_length(num):
> >        try:
> >            return num.bit_length()
> >        except AttributeError:
> >            # fallback implementation goes here
> >            ...
> >
> >
> >and then everywhere you want to write `n.bit_length()`, you write
> >`bit_length(n)` instead.
> >
> >Extension methods would let us do this:
> >
> >    # compatibility.py
> >    @extends(int):
> >    def bit_length(self):
> >        # fallback implementation goes here
> >             ...
> >
> >
> >    # mylibrary.py
> >    using compatibility
> >    num = 42
> >    num.bit_length()
> >
> >
> >Now obviously that isn't going to help with versions too old to
> >support extension methods, but eventually extension methods will
> >be available in the oldest version of Python you care about:
> >
> >    # supports Python 3.14 and above
> >
> >Once we reach that point, then backporting new methods to classes
> >becomes a simple matter of using an extension method. No mess, no 
> >fuss.
> >
> >As someone who has written a lot of code like that first bit_length
> >compatibility function in my time, I think I've just gone from "Yeah,
> >extension methods seem useful..." to "OMG I WANT THEM TEN YEARS AGO 
> >SO I
> >CAN USE THEM RIGHT NOW!!!".
> >
> >
> >Backporting might not be your killer-app for extension methods, but I
> >really do think they might be mine.
> >
> >
> >--
> >Steve
> >_______________________________________________
> >Python-ideas mailing list -- python-ideas@python.org 
> ><mailto:python-ideas@python.org>
> >To unsubscribe send an email to python-ideas-le...@python.org 
> ><mailto: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/UEERCY5L5MNFJCQQ4BTSCAHKRTOUXGGE/>
> >Code of Conduct: <http://python.org/psf/codeofconduct/>
> 



> Of course that means the the standard library might also introduce 
> something new that will be shadowed by one of your custom methods, 

Extension methods have lower priority than actual methods on the class. 
So that won't happen. The actual method on the class will shadow the 
extension method.

I'm not sure if you completely understand the use-case I was describing, 
so let me clarify for you with a concrete example.

Ints have a "bit_length" method, starting from Python 2.7. I needed to 
use that method going all the way back to version 2.4. I have an 
implementation that works, so I could backport that method to 2.4 
through 2.6, except that you can't monkey-patch builtins in Python.

So monkey-patching is out.

(And besides, I wouldn't want to monkey-patch it: I only need that 
method in one module. I want to localise the change to only where it is 
needed.)

Subclassing int wouldn't help. I need it to work on actual ints, and any 
third-party subclasses of int, not just my own custom subclass.

(And besides, have you tried to subclass int? It's a real PITA. It's 
easy enough to write a subclass, but every operation on it returns an 
actual int instead of the subclass. So you have to write a ton of 
boilerplate to make int subclasses workable. But I digress.)

So a subclass is not a good solution either.

That leaves only a function.

But that hurts code readability and maintainance. In 2.7 and above, 
bit_length is a method, not a function. All the documentation for 
bit_length assumes it is a method. Every tutorial that uses it has it as 
a method. Other code that uses it treats it as a method.

Except my code, where it is a function.

Using a function is not a *terrible* solution to the problem of 
backporting a new feature to older versions of Python. I've done it 
dozens of times and it's not awful. **But it could be better.**

Why can't the backport be a method, just like in 2.7 and above?

With extension methods, it can be.

Obviously not for Python 2.x code. But plan for the future: if we have 
extension methods in the language, eventually every version of Python we 
care about will support it. And then writing compatibility layers will 
be much simpler.


> and then you'll wish you had just used functions or a wrapper class.

Believe me, I won't. I've written dozens of compatibility functions over 
the last decade or more, going back to Python 2.3. I've written hybrid 
2/3 code. Extension methods would not always be useful, but for cases 
like int.bit_length, it would be a far superior solution.


> If you can import extension methods wholesale, you might even be 
> monkeypatching something without realising it

Extension methods is not monkey-patching. It is like adding a global 
name to one module. If I write:

    def func(arg):
        ...

in module A.py, that does not introduce func to any other module unless 
those other modules explicitly import it.

Extension methods are exactly analogous: they are only visible in the 
module where you opt-in to use them. They don't monkey-patch the entire 
interpreter-wide environment.

And because extension methods have a lower priority than actual methods, 
you cannot override an existing method on a class. You can only extend 
the class with a new method.



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

Reply via email to