Python class member lazy initialization

前端 未结 5 2080
Happy的楠姐
Happy的楠姐 2021-02-01 18:37

I would like to know what is the python way of initializing a class member but only when accessing it, if accessed. I tried the code below and it is working but is there somethi

5条回答
  •  北海茫月
    2021-02-01 19:11

    You could use a @property on the metaclass instead:

    class MyMetaClass(type):
        @property
        def my_data(cls):
            if getattr(cls, '_MY_DATA', None) is None:
                my_data = ...  # costly database call
                cls._MY_DATA = my_data
            return cls._MY_DATA
    
    
    class MyClass(metaclass=MyMetaClass):
        # ...
    

    This makes my_data an attribute on the class, so the expensive database call is postponed until you try to access MyClass.my_data. The result of the database call is cached by storing it in MyClass._MY_DATA, the call is only made once for the class.

    For Python 2, use class MyClass(object): and add a __metaclass__ = MyMetaClass attribute in the class definition body to attach the metaclass.

    Demo:

    >>> class MyMetaClass(type):
    ...     @property
    ...     def my_data(cls):
    ...         if getattr(cls, '_MY_DATA', None) is None:
    ...             print("costly database call executing")
    ...             my_data = 'bar'
    ...             cls._MY_DATA = my_data
    ...         return cls._MY_DATA
    ... 
    >>> class MyClass(metaclass=MyMetaClass):
    ...     pass
    ... 
    >>> MyClass.my_data
    costly database call executing
    'bar'
    >>> MyClass.my_data
    'bar'
    

    This works because a data descriptor like property is looked up on the parent type of an object; for classes that's type, and type can be extended by using metaclasses.

提交回复
热议问题