25

クラスFooには がありbar、アクセスされるまでロードされません。への以降のアクセスでbarオーバーヘッドが発生することはありません。

class Foo(object):

    def get_bar(self):
        print "initializing"
        self.bar = "12345"
        self.get_bar = self._get_bar
        return self.bar

    def _get_bar(self):
        print "accessing"
        return self.bar

ゲッターメソッドを使用する代わりに、プロパティまたは属性を使用して、このようなことを行うことは可能ですか?

目標は、後続のすべてのアクセスでオーバーヘッドなしで遅延ロードすることです...

4

3 に答える 3

14

もちろん、その後のアクセスで返されるインスタンス属性をプロパティに設定するだけです。

class Foo(object):
    _cached_bar = None 

    @property
    def bar(self):
        if not self._cached_bar:
            self._cached_bar = self._get_expensive_bar_expression()
        return self._cached_bar

記述子はpropertyデータ記述子 (および記述子フックを実装)__get__であるため、インスタンスに属性が存在する場合でも呼び出され、最終的に Python がその属性を無視するため、別の属性をテストする必要があります。各アクセス。__set____delete__bar

のみを実装する独自の記述子を作成できます。__get__その時点で、Python はインスタンスの属性が存在する場合は、記述子よりもその属性を使用します。

class CachedProperty(object):
    def __init__(self, func, name=None):
        self.func = func
        self.name = name if name is not None else func.__name__
        self.__doc__ = func.__doc__

    def __get__(self, instance, class_):
        if instance is None:
            return self
        res = self.func(instance)
        setattr(instance, self.name, res)
        return res

class Foo(object):
    @CachedProperty
    def bar(self):
        return self._get_expensive_bar_expression()

アプローチを好む場合__getattr__(それについて何か言いたいことがあります)、それは次のようになります。

class Foo(object):
    def __getattr__(self, name):
        if name == 'bar':
            bar = self.bar = self._get_expensive_bar_expression()
            return bar
        return super(Foo, self).__getattr__(name)

後続のアクセスではbar、インスタンスの属性が検出され、__getattr__参照されません。

デモ:

>>> class FooExpensive(object):
...     def _get_expensive_bar_expression(self):
...         print 'Doing something expensive'
...         return 'Spam ham & eggs'
... 
>>> class FooProperty(FooExpensive):
...     _cached_bar = None 
...     @property
...     def bar(self):
...         if not self._cached_bar:
...             self._cached_bar = self._get_expensive_bar_expression()
...         return self._cached_bar
... 
>>> f = FooProperty()
>>> f.bar
Doing something expensive
'Spam ham & eggs'
>>> f.bar
'Spam ham & eggs'
>>> vars(f)
{'_cached_bar': 'Spam ham & eggs'}
>>> class FooDescriptor(FooExpensive):
...     bar = CachedProperty(FooExpensive._get_expensive_bar_expression, 'bar')
... 
>>> f = FooDescriptor()
>>> f.bar
Doing something expensive
'Spam ham & eggs'
>>> f.bar
'Spam ham & eggs'
>>> vars(f)
{'bar': 'Spam ham & eggs'}

>>> class FooGetAttr(FooExpensive):
...     def __getattr__(self, name):
...         if name == 'bar':
...             bar = self.bar = self._get_expensive_bar_expression()
...             return bar
...         return super(Foo, self).__getatt__(name)
... 
>>> f = FooGetAttr()
>>> f.bar
Doing something expensive
'Spam ham & eggs'
>>> f.bar
'Spam ham & eggs'
>>> vars(f)
{'bar': 'Spam ham & eggs'}
于 2013-07-05T10:04:05.130 に答える