0

作成後に Python プロパティのゲッターを変更することはできますか?

class A:
    _lookup_str = 'hi'
    @property
    def thing():
        value = some_dictionary[_lookup_str]
        # overwrite self.thing so that it is just value, not a special getter
        return value

アイデアは、一度調べたら、もう一度調べる必要はないということです (辞書は決して変更されません)。私はそれをできた:

class A:
     _lookup_str = 'hi'
     _thing = None
     @property
     def thing():
         if not value:
             value = some_dictionary[_lookup_str]
         return value

しかし、そこでも条件をテストしています。これは、ゲッターを完全に削除して値に置き換えるよりも手間がかかります。

4

4 に答える 4

7

Werkzeug には、まさにあなたが望むことを行うcached_propertyデコレータがあります。__dict__最初の関数呼び出しの後の関数のエントリを最初の呼び出しの出力に置き換えるだけです。

コードは次のとおりです(githubのwerkzeug.utilsから、長さを少し編集しています):

_missing = object()

class cached_property(object):
    """A decorator that converts a function into a lazy property.  The
    function wrapped is called the first time to retrieve the result
    and then that calculated result is used the next time you access
    the value::

        class Foo(object):

            @cached_property
            def foo(self):
                # calculate something important here
                return 42

    The class has to have a `__dict__` in order for this property to
    work.
    """

    # implementation detail: this property is implemented as non-data
    # descriptor.  non-data descriptors are only invoked if there is
    # no entry with the same name in the instance's __dict__.
    # this allows us to completely get rid of the access function call
    # overhead.  If one choses to invoke __get__ by hand the property
    # will still work as expected because the lookup logic is replicated
    # in __get__ for manual invocation.

    def __init__(self, func, name=None, doc=None):
        self.__name__ = name or func.__name__
        self.__module__ = func.__module__
        self.__doc__ = doc or func.__doc__
        self.func = func

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        value = obj.__dict__.get(self.__name__, _missing)
        if value is _missing:
            value = self.func(obj)
            obj.__dict__[self.__name__] = value
        return value

(ところで、このようなコードやコードへのリンクを投稿する方が良いですか?)

これが機能する理由について詳しく知りたい場合は、記述子に関する Python ドキュメントを参照してください。@property上記のコードは、上書き可能な非データ記述子 ( とは異なります) を作成します。

于 2012-08-23T03:58:05.673 に答える
0

推奨されていませんが、古いスタイルのクラスで機能します。

>>> class A:
...   @property
...   def thing(self):
...       print 'thing'
...       self.thing = 42
...       return self.thing
... 
>>> a = A()
>>> a.thing
thing
42
>>> a.thing
42
>>> a.thing
42

新しいスタイルのクラス(型、オブジェクトのサブクラス)では機能しないため、すべてのクラスが新しいスタイルであるPython3では機能しません。この場合、@JeffTratnerの回答を使用してください。

于 2012-08-23T05:10:23.240 に答える
0

答えは、Jeff Tratner が示したよう__dict__に、python オブジェクトの にあるプロパティ オブジェクトを上書きすることです。Werkzeug の cached_property は私には複雑すぎるようです。次の(はるかに単純な)コードは私にとってはうまくいきます:

def cached_property(f):
    @property
    def g(self, *args, **kwargs):
        print 'trace'
        value = f(self, *args, **kwargs)
        self.__dict__[f.__name__] = value
        return value
    return g

class A:
    @cached_property
    def thing(self):
        return 5

a = A()
print a.thing
print a.thing
print a.thing
print a.thing

# 'trace' is only shown once -- the first time a.thing is accessed.
于 2012-08-23T04:59:20.680 に答える
0

JF Sebastian と Isaac Sutherland による回答は、新しいスタイル クラスでは機能しません。Jeff Tratner が言及した結果が生成されます。アクセスごとにトレースを出力します。

新しいスタイル クラスの場合は、オーバーライドする必要があります__getattribute__

簡単な修正:

def cached_property(f):
    @property
    def g(self, *args, **kwargs):
        print 'trace'
        value = f(self, *args, **kwargs)
        self.__dict__[f.__name__] = value

        return value
    return g

def cached_class(c):
    def d(self, name):
        getattr = object.__getattribute__
        if (name in getattr(self, '__dict__')):
            return getattr(self, '__dict__')[name]
        return getattr(self, name)

    c.__getattribute__ = d
    return c

@cached_class
class A(object):
    @cached_property
    def thing(self):
        return 5

a = A()
print a.thing
print a.thing
print a.thing
print a.thing
于 2012-08-23T06:59:26.613 に答える