[Python-Dev] PEP 562

Serhiy Storchaka storchaka at gmail.com
Wed Nov 15 06:59:55 EST 2017


15.11.17 12:53, Ivan Levkivskyi пише:
> On 15 November 2017 at 08:43, Serhiy Storchaka <storchaka at gmail.com 
> <mailto:storchaka at gmail.com>> wrote:
> 
>     It is worth to mention that using name as a module global will
>     bypass __getattr__. And this is intentional, otherwise calling
>     __getattr__ for builtins will harm a performance.
> 
> 
> Good point!

And please document idiomatic way of using a module global with 
triggering __getattr__. For example if you want to use a lazy loaded 
submodule.

     sys.modules[__name__].foobar

or

     from . import foobar

The difference between them that the latter sets the module attribute, 
thus __getattr__ will be called only once.

>         Backwards compatibility and impact on performance
>         =================================================
> 
> 
>     What is affect on pydoc, word completion, inspect, pkgutil, unittest?
> 
> 
> This is rather gray area. I am not sure that we need to update them in 
> any way, just the people who use __getattr__ should be aware that
> some tools might not yet expect it.. I will add a note to the PEP about 
> this.

This problem is not new, since it was possible to replace a module with 
a module subclass with overridden __getattr__ and __dir__ before, but 
now this problem can occur more often.

>     I would create more standard helpers (for deprecation, for lazy
>     importing). This feature is helpful not by itself, but because it
>     will be used for implementing new features. Using __getattr__
>     directly will need to write a boilerplate code. Maybe when
>     implementing these helper you will discover that this PEP needs some
>     additions.
> 
> 
> 
> But in which module these helpers should live?

Good question. lazy_import() could be added in importlib (or 
importlib.util?). The helper that just adds deprecation on importing a 
name, could be added in importlib too. But I think that it would be 
better if the deprecated() helper will also create a wrapper that raises 
a deprecation warning on the use of deprecated function. It could be 
added in the warnings or functools modules.

I would add also a more general lazy_initialized(). It is something like 
cached module property. Executes the specified code on first use, and 
cache the result as a module attribute.

In all these cases the final __getattr__ method should be automatically 
constructed from different chunks. At the end it could call a user 
supplied __getattr__. Or maybe the module method __getattr__ should look 
first at special registry before calling the instance attribute 
__getattr__()?

     def ModuleType.__getattr__(self, name):
         if name in self.__properties__:
	    call self.__properties__[name]()
         elif '__getattr__' in self.__dict__:
             call self.__dict__['__getattr__'](name)
         else:
             raise AttributeError

I'm wondering if the __set_name__ mechanism can be extended to modules. 
What if call the __set_name__() method for all items in a module dict 
after finishing importing the module?



More information about the Python-Dev mailing list