tl; dr *Maybe* staticmethod could be modified to become callable?

io.open is a built-in function (type "builtin_function_or_method"). It
behaves differently than _pyio.open which is a Python function (type
"function").

The difference is in the LOAD_METHOD bytecode which uses __get__()
descriptor if available. Built-in function has no __get__() method and
so the function is used directly as a method. Python function has a
__get__() descriptor which returns the function unchanged when a class
method is requested, and create a bound method if an instance method
is requested:
---
def func():
    ...

FunctionType = type(func)

class MyClass:
    method = func

class_method = MyClass.method
assert class_method is func
assert class_method is FunctionType.__get__(func, None, MyClass)

obj = MyClass()
instance_method = obj.method
assert instance_method.__self__ is obj
assert instance_method.__func__ is func
# each __get__() call creates a new bound method
assert instance_method == FunctionType.__get__(func, obj, type(obj))
---

@staticmethod decorator avoids the creation of the bound method:
---
def func(): ...

class MyClass:
    method = staticmethod(func)

# method = MyClass.method
attr = MyClass.__dict__['method']
method = type(attr).__get__(attr, None, MyClass)
assert method is func
---

The drawback is that the object created by staticmethod cannot be
called :-( The following code raises a TypeError:
---
wrapped = staticmethod(func)
wrapped()
---

*Maybe* staticmethod could be modified to become callable?

Victor


On Wed, Mar 31, 2021 at 2:34 PM Victor Stinner <vstin...@python.org> wrote:
>
> Hi,
>
> The io module provides an open() function. It also provides an
> OpenWrapper which only exists to be able to store open as a method
> (class or instance method). In the _pyio module, pure Python
> implementation of the io module, OpenWrapper is implemented as:
>
> class OpenWrapper:
>     """Wrapper for builtins.open
>
>     Trick so that open won't become a bound method when stored
>     as a class variable (as dbm.dumb does).
>
>     See initstdio() in Python/pylifecycle.c.
>     """
>     def __new__(cls, *args, **kwargs):
>         return open(*args, **kwargs)
>
> I would like to remove this class which is causing troubles in the PEP
> 597 implementation, but I don't know how. Simplified problem:
> ---
> def func():
>     print("my func")
>
> class MyClass:
>     method = func
>
> func() # A
> MyClass.method() # B
> obj = MyClass()
> obj.method() # C
> ---
>
> With this syntax, A and B work, but C fails with TypeError: func()
> takes 0 positional arguments but 1 was given.
>
> If I decorate func() with @staticmethod, B and C work, but A fails
> with TypeError: 'staticmethod' object is not callable.
>
> Is OpenWrapper the only way to have a callable object which works in
> the 3 variants A, B and C?
>
> A, B and C work if MyClass is modified to use staticmethod:
>
> class MyClass:
>     method = staticmethod(func)
>
> Victor
> --
> Night gathers, and now my watch begins. It shall not end until my death.



-- 
Night gathers, and now my watch begins. It shall not end until my death.
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/KAZ5HR2ZLPBZ76FZZOZI5RU35FYDBDI7/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to