26

クラスメンバーを初期化するPythonの方法を知りたいのですが、アクセスした場合にのみアクセスします。以下のコードを試してみましたが、動作していますが、それよりも簡単なものはありますか?

class MyClass(object):

    _MY_DATA = None

    @staticmethod
    def _retrieve_my_data():
        my_data = ...  # costly database call
        return my_data

    @classmethod
    def get_my_data(cls):
        if cls._MY_DATA is None:
            cls._MY_DATA = MyClass._retrieve_my_data()
        return cls._MY_DATA
4

5 に答える 5

42

代わりにメタクラス@propertyで aを使用できます。

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):
    # ...

これによりmy_data、クラスに属性が作成されるため、コストのかかるデータベース呼び出しは、アクセスを試行するまで延期されますMyClass.my_data。データベース呼び出しの結果は、 に格納することでキャッシュされます。呼び出しはクラスに対して1 回MyClass._MY_DATAだけ行われます。

Python 2 の場合、クラス定義本体に属性class MyClass(object):を使用して追加し、メタクラスをアタッチします。__metaclass__ = MyMetaClass

デモ:

>>> 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'

これが機能するpropertyのは、オブジェクトの親型で検索されるようなデータ記述子です。typeであり、typeメタクラスを使用して拡張できるクラスの場合。

于 2013-03-05T14:50:47.163 に答える
7

コードをよりクリーンにする別のアプローチは、目的のロジックを実行するラッパー関数を作成することです。

def memoize(f):
    def wrapped(*args, **kwargs):
        if hasattr(wrapped, '_cached_val'):
            return wrapped._cached_val
        result = f(*args, **kwargs)
        wrapped._cached_val = result
        return result
    return wrapped

次のように使用できます。

@memoize
def expensive_function():
    print "Computing expensive function..."
    import time
    time.sleep(1)
    return 400

print expensive_function()
print expensive_function()
print expensive_function()

どの出力:

Computing expensive function...
400
400
400

これで、クラスメソッドは次のようになります。次に例を示します。

class MyClass(object):
        @classmethod
        @memoize
        def retrieve_data(cls):
            print "Computing data"
            import time
            time.sleep(1) #costly DB call
            my_data = 40
            return my_data

print MyClass.retrieve_data()
print MyClass.retrieve_data()
print MyClass.retrieve_data()

出力:

Computing data
40
40
40

これにより、関数への引数のセットに対して1つの値のみがキャッシュされるため、入力値に応じて異なる値を計算する場合はmemoize、もう少し複雑にする必要があることに注意してください。

于 2013-03-05T15:01:29.543 に答える