10

dictやその他のオブジェクトを受け入れるメソッドと、それらのオブジェクトからフェッチする「フィールド」の名前があります。オブジェクトがdictの場合、メソッドは__getitem__名前付きキーを取得するために使用します。そうでない場合はgetattr、名前付き属性を取得するために使用します。これは、Webテンプレート言語ではかなり一般的です。たとえば、カメレオンテンプレートでは、次のようになります。

<p tal:content="foo.keyname">Stuff goes here</p>

fooのようなdictとして渡す場合は、「keyname」キーをフェッチして「bar」を取得します{'keyname':'bar'}。が次のようなクラスのインスタンスである場合foo.keynamefoo

class Foo(object):
    keyname = 'baz'

次にfoo.keyname、属性から値をフェッチしkeynameます。カメレオン自体は、次のchameleon.py26ようにその機能を(モジュール内で)実装します。

def lookup_attr(obj, key):
    try:
        return getattr(obj, key)
    except AttributeError as exc:
        try:
            get = obj.__getitem__
        except AttributeError:
            raise exc
        try:
            return get(key)
        except KeyError:
            raise exc

私はそれを次のような自分のパッケージに実装しました:

try:
    value = obj[attribute]
except (KeyError, TypeError):
    value = getattr(obj, attribute)

問題は、それはかなり一般的なパターンです。多くのモジュールで、その方法またはそれに非常によく似た方法を見てきました。では、なぜ言語のコア、または少なくともコアモジュールの1つにそのようなものがないのですか?それができない場合、それをどのように書くべきかについての決定的な方法はありますか?

4

3 に答える 3

16

私はあなたの質問を半分読んで、以下を書き、それからあなたの質問を読み直して、私が微妙に異なる質問に答えたことに気づきました。しかし、私は以下が実際にソート後の答えを提供していると思います。そう思わない場合は、代わりに、このより一般的な質問をしたふりをしてください。これには、サブ質問としてあなたの質問が含まれていると思います。

「Pythonが属性とアイテムを交換可能として扱う組み込みの方法を提供しないのはなぜですか?」


私はこの質問についてかなり考えましたが、答えは非常に簡単だと思います。コンテナタイプを作成するときは、属性アイテムを区別することが非常に重要です。適度に開発されたコンテナタイプには、コンテンツを適切に管理できるようにする多くの属性(多くの場合、メソッドであるとは限りません)があります。したがって、たとえば、dictには、、、itemsなどvalueskeysありiterkeysます。これらの属性はすべて、.表記法を使用してアクセスされます。一方、アイテムは[]表記法を使用してアクセスされます。したがって、衝突は発生しません。

.表記を使用してアイテムアクセスを有効にするとどうなりますか?突然、名前空間が重複しています。今、衝突をどのように処理していますか?dictをサブクラス化してこの機能を与えると、ルールのようにキーを使用できないかitems、ある種の名前空間階層を作成する必要があります。最初のオプションは、面倒で、従うのが難しく、強制するのが難しいルールを作成します。items2番目のオプションは、衝突の問題を完全に解決することなく、厄介な量の複雑さを生み出します。これは、アイテムitemsと属性のどちらが必要かを指定するための代替インターフェイスが必要だからです。

現在、特定の種類の非常にプリミティブなタイプの場合、これは許容されます。namedtupleたとえば、標準ライブラリにあるのはおそらくそのためです。(ただしnamedtuple、これらの問題が発生する可能性があることに注意してください。これが、ファクトリ関数として実装され(継承を防止)、次のような奇妙なプライベートメソッド名を使用する理由_asdictです。)

objectまた、(パブリック)属性のないサブクラスを作成して使用するのも非常に簡単setattrです。、、をオーバーライドし、、、を呼び出すことも非常に簡単__getitem__です__setitem__。その__delitem__ため、アイテムへのアクセスは、、などの構文糖衣になります(ただし、予期しない動作が発生するため、少し疑問があります)。__getattribute____setattr____delattr__getattr()setattr()

しかし、拡張して継承できるようにし、新しい有用な属性を追加したい、あらゆる種類の十分に開発されたコンテナクラスの場合、__getattr__ + __getitem__ハイブリッドは、率直に言って、巨大なPITAになります。

于 2011-07-18T20:03:10.943 に答える
5

Python標準ライブラリで最も近いものはnamedtuple()、http: //docs.python.org/dev/library/collections.html#collections.namedtupleです。

Foo = namedtuple('Foo', ['key', 'attribute'])
foo = Foo(5, attribute=13)
print foo[1]
print foo.key

または、常に実際にそのdictに格納するが、属性の設定と取得の外観を許可する独自のタイプを簡単に定義できます。

class MyDict(dict):
    def __getattr__(self, attr):
        return self[attr]
    def __setattr__(self, attr, value):
        self[attr] = value

d = MyDict()

d.a = 3
d[3] = 'a'
print(d['a']) # 3
print(d[3]) # 'a'
print(d['b']) # Returns a keyerror

d.3ただし、これは構文エラーであるため、実行しないでください。もちろん、このようなハイブリッドストレージタイプを作成する方法はもっと複雑です。多くの例をWebで検索してください。

両方をチェックする方法に関しては、カメレオンの方法は徹底的に見えます。「標準ライブラリに両方を実行する方法がないのはなぜですか」ということになると、あいまいさが悪いからです。はい、Pythonにはダックタイピングや他のすべての種類のマスカレードがあり、クラスは実際には単なる辞書ですが、ある時点で、辞書やリストなどのコンテナから、クラスから必要な機能とは異なる機能が必要になります。メソッドの解決順序が必要です。 、オーバーライドなど。

于 2011-07-18T19:37:39.073 に答える
4

dictこのようにネイティブに動作する独自のサブクラスを非常に簡単に作成できます。私が属性の「山」と呼ぶのが好きな最小限の実装は、次のようになります。

class Pile(dict):
    def __getattr__(self, key):
        return self[key]
    def __setattr__(self, key, value):
        self[key] = value

残念ながら、オブジェクトを最初から制御するのではなく、渡された辞書または属性を含むオブジェクトのいずれかを処理できるようにする必要がある場合、これは役に立ちません。

あなたの状況では、私はおそらくあなたが持っているものと非常によく似たものを使用しますが、それを関数に分割することを除いて、私はそれをいつも繰り返す必要はありません。

于 2011-07-18T19:28:13.797 に答える