10

Is it possible to have a static property on a class that would be computed as a one off. The idea would be to be able to do it like so:

class Foo:
    static_prop = Foo.one_off_static_method()

    @staticmethod
    def one_off_static_method():
        return 'bar'

I thought of using __new__ as well.

Class Foo:
    def __new__(cls):
         cls.static_prop = ... do everything here

Not sure the implications of that though.

mkohram
  • 305
  • 2
  • 12

4 Answers4

6

If you want it computed at class definition time, see chepner's answer - although I would recommend just to use a module level function instead.

If you want it lazily evaluated, per instance, then you might be interested in functools.cached_property.

>>> from random import random
>>> from functools import cached_property
>>> class Foo(object):
...     @cached_property
...     def one_off_thing(self):
...         print("computing...")
...         return random()
...     
>>> foo = Foo()
>>> foo.one_off_thing
computing...
0.5804382038855782
>>> foo.one_off_thing
0.5804382038855782

Note: stdlib functools.cached_property requires Python 3.8+, for Python < 3.8 you can pip install cached_property.

wim
  • 338,267
  • 99
  • 616
  • 750
3

Here you go, I made a small descriptor for you :-)

Upon accessing the attribute, it will be computed and cached.

class CachedStaticProperty:
    """Works like @property and @staticmethod combined"""

    def __init__(self, func):
        self.func = func

    def __get__(self, inst, owner):
        result = self.func()
        setattr(owner, self.func.__name__, result)
        return result

The way it works is rather simple:

  1. Upon using the decorator syntax, I save the function internally.
  2. Upon access, I call the function and set the value as the class value with the same name as the original function.

That's all there is to it. Simple and efficient.

Bharel
  • 23,672
  • 5
  • 40
  • 80
1

Until the class is actually created, one_off_static_method is just a regular function. It needs to be defined before you attempt to call it, since you want to call it while the class statement is being executed. Once you are done with it, you can simply delete it.

class Foo:
    def _one_off_static_method():
        return 'bar'

    static_prop = _one_off_static_method()
    del _one_off_static_method
chepner
  • 497,756
  • 71
  • 530
  • 681
1

Starting in Python 3.8 there will be a cached_property decorator included in the Python builtin/core functools library.

https://docs.python.org/dev/library/functools.html?highlight=s#functools.cached_property

Other questions on this website have similar answers. Some highlights are that one can do

@property
@functools.lru_cache(1)

(both Python builtins) for a similar effect. And that some web server libraries like Flask or Werkzeug already include their own implementations of cached_property decorators.

There are of course other ways (as mentioned in the other answers to this question and the answers to other questions) that one can create their own custom cached property decorators.

python: are property fields being cached automatically?
Is there a decorator to simply cache function return values?