10

tl; dr:プロパティデコレータがクラスレベルの関数定義で機能するのに、モジュールレベルの定義では機能しないのはなぜですか?

私はいくつかのモジュールレベルの関数にプロパティデコレータを適用していました。それらは単なる属性ルックアップによってメソッドを呼び出すことができると考えていました。

これは特に魅力的でした。なぜなら、、などの構成関数のセットを定義していたからですget_port。これらの関数get_hostnameはすべて、より単純で簡潔なプロパティの対応物portであるhostname、などに置き換えることができました。

したがって、config.get_port()はるかに良いでしょうconfig.port

次のトレースバックを見つけたとき、私は驚きました。これは実行可能なオプションではないことを証明しました。

TypeError: int() argument must be a string or a number, not 'property'

エレガントでハッキーなpbsライブラリを使用してシェルコマンドをスクリプト化するために使用していたので、モジュールレベルでプロパティのような機能の前例を見たことがあることを私は知っていました。

以下の興味深いハックは、pbsライブラリのソースコードにあります。モジュールレベルでプロパティのような属性ルックアップを実行する機能を有効にしますが、それは恐ろしく、恐ろしくハックです。

# this is a thin wrapper around THIS module (we patch sys.modules[__name__]).
# this is in the case that the user does a "from pbs import whatever"
# in other words, they only want to import certain programs, not the whole
# system PATH worth of commands.  in this case, we just proxy the
# import lookup to our Environment class
class SelfWrapper(ModuleType):
    def __init__(self, self_module):
        # this is super ugly to have to copy attributes like this,
        # but it seems to be the only way to make reload() behave
        # nicely.  if i make these attributes dynamic lookups in
        # __getattr__, reload sometimes chokes in weird ways...
        for attr in ["__builtins__", "__doc__", "__name__", "__package__"]:
            setattr(self, attr, getattr(self_module, attr))

        self.self_module = self_module
        self.env = Environment(globals())

    def __getattr__(self, name):
        return self.env[name]

以下は、このクラスをインポート名前空間に挿入するためのコードです。実際にsys.modules直接パッチを当てます!

# we're being run as a stand-alone script, fire up a REPL
if __name__ == "__main__":
    globs = globals()
    f_globals = {}
    for k in ["__builtins__", "__doc__", "__name__", "__package__"]:
        f_globals[k] = globs[k]
    env = Environment(f_globals)
    run_repl(env)

# we're being imported from somewhere
else:
    self = sys.modules[__name__]
    sys.modules[__name__] = SelfWrapper(self)

どのくらいの長さpbsを経なければならないかを見たので、なぜこのPythonの機能が言語に直接組み込まれていないのか疑問に思います。特にデコレータは、そのpropertyような機能を追加するための自然な場所のようです。

これが直接組み込まれていない理由や動機はありますか?

4

2 に答える 2

8

これは、2 つの要因の組み合わせに関連しています。1 つ目は、プロパティが記述子プロトコルを使用して実装されていること、2 つ目は、モジュールがインスタンス化可能なクラスではなく、常に特定のクラスのインスタンスであることです。

記述子プロトコルのこの部分は に実装されてobject.__getattribute__います (関連するコードはPyObject_GenericGetAttr1319 行目から始まります)。検索ルールは次のようになります。

  1. クラスmroを検索して、型辞書を探します。name
  2. 最初に一致した項目がデータ記述子の場合、それを呼び出して__get__結果を返します
  3. name がインスタンス ディクショナリにある場合は、関連付けられた値を返します
  4. クラス辞書から一致する項目があり、それが非データ記述子であった場合、それを呼び出し__get__て結果を返します
  5. クラス辞書から一致するアイテムがあった場合は、それを返します
  6. raise AttributeError

これの鍵は 3 番にあります - がインスタンス ディクショナリnameで見つかった場合(モジュールの場合と同様)、その値が返されるだけです - 記述子性のテストは行われず、呼び出されません。これにより、次の状況が発生します (Python 3 を使用):__get__

>>> class F:
...    def __getattribute__(self, attr):
...      print('hi')
...      return object.__getattribute__(self, attr)
... 
>>> f = F()
>>> f.blah = property(lambda: 5)
>>> f.blah
hi
<property object at 0xbfa1b0>

呼び出されて.__getattribute__ いることがわかりますが、記述子として扱われていません。f.blah

ルールがこのように構成されている理由は、インスタンス (したがってモジュール内) で記述子を許可することの有用性と、これがもたらす余分なコードの複雑さとの間の明示的なトレードオフである可能性があります。

于 2012-08-01T05:55:23.873 に答える
0

プロパティはクラス (特に新しいスタイルのクラス) に固有の機能であるため、プロパティ デコレータはクラス メソッドにのみ適用できます。

新しいスタイルのクラスは、オブジェクトから派生したものです。つまり、クラス Foo(object):

詳細情報:モジュールはオブジェクトと同じようにプロパティを持つことができますか?

于 2012-08-01T04:29:54.427 に答える