4

PEP 3115で説明されている「順序付きクラス」 (つまり、メンバーが宣言された順序でアクセスできるクラス)を使用しようとしています。そこに与えられた実装は

# The custom dictionary
class member_table(dict):
    def __init__(self):
        self.member_names = []

    def __setitem__(self, key, value):
        # if the key is not already defined, add to the
        # list of keys.
        if key not in self:
            self.member_names.append(key)

        # Call superclass
        dict.__setitem__(self, key, value)

# The metaclass
class OrderedClass(type):

    # The prepare function
    @classmethod
    def __prepare__(metacls, name, bases): # No keywords in this case
        return member_table()

    # The metaclass invocation
    def __new__(cls, name, bases, classdict):
        # Note that we replace the classdict with a regular
        # dict before passing it to the superclass, so that we
        # don't continue to record member names after the class
        # has been created.
        result = type.__new__(cls, name, bases, dict(classdict))
        result.member_names = classdict.member_names
        return result

class MyClass(metaclass=OrderedClass):
    # method1 goes in array element 0
    def method1(self):
        pass

    # method2 goes in array element 1
    def method2(self):
        pass

私が混乱していることがいくつかあります。まず、 である理由__prepare__はありclassmethodますか?定義は使用しませんmetacls-これは単なる慣習ですか?

第二に、このコードを試してみると'__module__'MyClass.member_names最初'method1'の要素'method2'であると主張するコメントと矛盾しているように見えます。'method1'この特別な属性がリストに含まれるのに、他の属性が含まれないのはなぜですか? 私を驚かせる可能性のあるものは他にありますか (__doc__クラスに docstring があり、明示的に定義したものを除く)?

member_names最後に、この実装は基本クラスから を取得しません。それを達成したい場合、次の変更に何か問題がありますか__prepare__(重複をチェックしないという事実を除いて)?

@classmethod
def __prepare__(metacls, name, bases):
    prep_dict = member_table()
    for base in bases:
        try:
            prep_dict.member_names.extend(base.member_names)
        except AttributeError:
            pass
    return prep_dict
4

1 に答える 1

5

まず、クラスメソッドである理由__prepare__はありますか? 定義は metacls を使用しません。これは単なる規則ですか?

コメントで指摘されているように、__prepare__が呼び出されたとき、クラス自体はまだインスタンス化されていません。つまり、通常のメソッド (最初のパラメーターとして) の場合、この呼び出しでselfになる値はありません。self

そして、クラス本体の解析で通常の dict の代わりに使用される dict のようなオブジェクトを返すことであるため、それは理にかなって__prepare__います-したがって、作成されるクラスにはまったく依存しません。

コメントで述べたように、 (最初のパラメーターstaticmethodを取得しないことを意味する) 場合、メタクラスの他のメソッドにアクセスできなくなります。これは不必要に制限されます。metacls__prepare__

そして、そうです、 と同様selfmetacls、この場合、 は慣例にすぎません - 通常の Python 識別子です。cls(クラスメソッドが通常のクラスで使用される場合、通常、最初のパラメータはの代わりに示されることに注意してくださいmetacls。)

第二に、このコードを試してみると__module__、最終的に MyClass.member_names と の前にmethod1なり、最初の要素method2であると主張するコメントと明らかに矛盾しています。method1この特別な属性がリストに含まれるのに、他の属性が含まれないのはなぜですか? 私を驚かせる可能性のあるものは他にありますか (__doc__クラスに docstring があり、明示的に定義したものを除く)?

おそらく、メタクラス__module__のメソッドを考えた後で、クラス用の特別なメンバーが考えられたからでしょう。__prepare__(PEP の定義をグーグルで確認することはできませんでした__module__が、これが事実であるに違いありません。)

いいえ、将来のバージョンの Python が、辞書で明示的なクラス メンバーの前に来る可能性のあるクラスに、魔法の属性を追加しないという保証はありません。

member_tableしかし、メタクラスを使用すると、完全に制御できます。たとえば、「__」で始まる属性をカウントしないように、dict のようなオブジェクト (コード内) を調整するだけです。または、最終的なクラス インスタンスにまったく追加しないこともできます (この方法で定義されたクラスが特定の Python 機能で動作しないリスクがあります)。

最後に、この実装は基本クラスから member_names を取得しません。それを達成したい場合、次の変更に何か問題があります__prepare__か?

それを読んで、提案された実装に問題はないと思いますが、もちろんテストする必要があります。

更新(2013-06-30): このテーマは Python 開発者リストで活発に議論されており、Python 3.4 からはすべてのクラスがデフォルトで順序付けられるようになり、メタクラスを使用する必要がないか__prepare__、順序付けのみが必要になるようです。

于 2011-12-08T02:50:55.453 に答える