python 實現類屬性的懶載入裝飾器

一代咩神發表於2021-04-25
class lazy:
    # 告訴 Python 不要建立物件的 __dict__,而是隻給一個固定集合的屬性分配空間。
    __slots__ = ['func']

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

def __get__(self, instance, owner):
    name = self.func.__name__
    # 如果 func 不為 None 則賦值給 owner (這裡是 `MyClass`) 的 `name` 屬性,
    # 此後 owner 的 name() 不再是裝飾器方法,而是實實在在的 func() 方法返回值!
    if (v := self.func(owner)) is not None:
        setattr(owner, name, v)
    return owner.__dict__.get(name) if v else None

class MyClass:
    @lazy
    def name(self) -> str:
        print("debug: name(self)")
        return "get name"

if __name__ == '__main__':
    name = MyClass.name  # output: 'debug: name(self)'
    print(f'[{name}]', type(name))  # => [get name] <class 'str'>
    print(f'[{MyClass.name}]')  # => [get name]

第一次呼叫 MyClass.name 時會進入到裝飾器的 __get__ 方法中返回值,我們在 __get__ 方法的內部再重新把裝飾器的 func 返回值賦值給 owner ( 這裡是 MyClass ) 的 name 屬性,從而達到 替換值 的概念。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章