[Python-ideas] Defer Statement

Nick Coghlan ncoghlan at gmail.com
Sun Jun 4 04:12:20 EDT 2017


On 4 June 2017 at 17:37, Nathaniel Smith <njs at pobox.com> wrote:
> I think in general I'd recommend making the API for accessing these
> things be a function call interface, so that it's obvious to the
> caller that some expensive computation might be going on. But if
> you're stuck with an attribute-lookup based interface, then you can
> use a __getattr__ hook to compute them the first time they're
> accessed:
>
> class LazyConstants:
>     def __getattr__(self, name):
>         value = compute_value_for(name)
>         setattr(self, name, value)
>         return value
>
> __getattr__ is only called as a fallback, so by setting the computed
> value on the object we make any future attribute lookups just as cheap
> as they would be otherwise.
>
> You can get this behavior onto a module object by doing
> "sys.modules[__name__] = Constants()" inside the module body, or by
> using a <del>hack</del> elegant bit of code like
> https://github.com/njsmith/metamodule/ (mostly the latter would only
> be preferred if you have a bunch of other attributes exported from
> this same module and trying to move all of them onto the LazyConstants
> object would be difficult).

This reminds me: we could really use some documentation help in
relation to https://bugs.python.org/issue22986 making module __class__
attributes mutable in Python 3.5+

At the moment, that is just reported in Misc/NEWS as "Issue #22986:
Allow changing an object's __class__ between a dynamic type and static
type in some cases.", which doesn't do anything to convey the
significant *implications* of now being able to define module level
properties as follows:

    >>> x = 10
    >>> x
    10
    >>> import __main__ as main
    >>> main.x
    10
    >>> from types import ModuleType
    >>> class SpecialMod(ModuleType):
    ...     @property
    ...     def x(self):
    ...         return 42
    ...
    >>> main.__class__ = SpecialMod
    >>> x
    10
    >>> main.x
    42

(I know that's what metamodule does under the hood, but if the 3.5+
only limitation is acceptable, then it's likely to be clearer to just
do this inline rather than hiding it behind a 3rd party API)

One potentially good option would be a HOWTO guide on "Lazy attribute
initialization" in https://docs.python.org/3/howto/index.html that
walked through from the basics of using read-only properties with
double-underscore prefixed result caching, through helper functions &
methods decorated with lru_cache, and all the way up to using
__class__ assignment to enable the definition of module level
properties.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-ideas mailing list