2017-10-19 17 views
1

私は__prepare__機能について教えています。 result.member_names = classdict.member_namesメタクラスと__prepare__()

どの変数classdictmember_tableクラスから属性を取得できます?そして、私は私の質問は、このラインであるPEP3115

# 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__関数はmember_tableのインスタンスを返しますが、member_table()classdict.member_namesの間のリンクはどのように生成されますか?

多くの皆様に感謝します。

答えて

2

これはまさに準備がしているので、かなり簡単です。

3.3.3.3。クラスネームスペースの準備適切なメタクラスが特定されると、クラスネームスペースが準備されます。 メタクラスに__prepare__属性がある場合は、namespace = metaclass.__prepare__(name, bases, **kwds)(追加の キーワード引数がある場合はクラス定義に由来します)と呼ばれます。

メタクラスに__prepare__属性がない場合、クラス の名前空間は、空の順序付きマッピングとして初期化されます。意味

https://docs.python.org/3/reference/datamodel.html#preparing-the-class-namespace

、メタクラス__new____init__メソッドに渡されclassdict属性が__prepare__によって返されるまったく同じオブジェクトです。

このオブジェクトはマッピングインスタンス、つまりdictのように動作し、少なくとも__setitem__メソッドを持つオブジェクトである必要があります。この__setitem__メソッドは、宣言されたクラス本体の内部で設定されたすべての変数に対してPythonによって呼び出されます。

つまり、カスタムメタクラスを持たない通常のクラスの場合、変数は辞書(Python 3.6以降の順序付き辞書)に記録されます。

これは、Pythonが各文をクラス本体内で実行するときに起こります。この機能が最初に設計されたとき

In [21]: class M(type): 
    ...:  def __prepare__(self, *args): 
    ...:   class CustomDict(dict): 
    ...:    __repr__ = lambda self: "I am a custom dict: " + str(id(self)) 
    ...:   namespace = CustomDict() 
    ...:   print("From __prepare__", namespace) 
    ...:   return namespace 
    ...: 
    ...:  def __new__(metacls, name, bases, namespace): 
    ...:   print("From __new__:", namespace) 
    ...:   return super().__new__(metacls, name, bases, namespace) 
    ...:  
    ...:  

In [22]: class Test(metaclass=M): 
    ...:  def __init__(self): 
    ...:   ... 
    ...:  print("From class body:", locals(), locals()["__init__"]) 
    ...:  
    ...:  
From __prepare__ I am a custom dict: 140560887720440 
From class body: I am a custom dict: 140560887720440 <function Test.__init__ at 0x7fd6e1bd7158> 
From __new__: I am a custom dict: 140560887720440 

メインユースケース、おそらく正確に意味のあるクラス本体の内部で宣言の順序を作る可能性があった。これは、1つのクラス本体の内部でlocals()を呼び出す必要が返されるのと同じオブジェクトであります。つまり、__prepare__メソッドは、collections.OrderedDictインスタンスを返すだけで、__new__または__init__がその順序で動作します。 Python 3.6以降、クラス属性の順序付けはデフォルトで行われています。そして、__prepare__の機能は高度なままであり、実際にはそれを使用することを考える必要があります。

+0

あなたの答えをありがとう!もう少し小さな疑問 - '__prepare__'関数が常にdictまたは修飾dict型(他の型ではない)である名前空間を返すことを確認できますか? – luoshao23

+0

「確認する」とはどういう意味ですか?あなたは '__prepare__'をオーサリングし、それが何を返すのか知っているべきです。完全なマッピングオブジェクトのように動作する必要がない場合は、 '__setitem__'だけが必要で、mclass' __init__'のコードはそれに対処する方法を知っています。 – jsbueno

+0

(あなたの質問に答えられた場合は、その答えを忘れないでください) – jsbueno

関連する問題