2017-03-16 9 views
3

こんにちはStackoverflowのコミュニティうわーはsix.with_metaclass()の仕事をしていますか?

私はDjango(とWagtailのストリームフィールド)がボンネットの下でどのように動作するかを理解しようとしています。それをやって、メタクラスについて学び、原則を扱うと信じています。つまり、ちょうどSIXがwith_metaclass関数をどのように実行するかは、まだ私にとってはあまりあいまいです。ここでは具体的な質問が続いコードです:

models.pyは

class BlogPage(Page): 
    blogElement = StreamField([ 
     ('heading', blocks.CharBlock(classname="full title")), 
     ('paragraph', blocks.TextBlock()), 
     ('picture', ImageChooserBlock()), 
    ], default=[]) 

wagtailcore> fields.py

class StreamField(models.Field): 
    def __init__(self, block_types, **kwargs): 
     if isinstance(block_types, Block): 
      self.stream_block = block_types 
     elif isinstance(block_types, type): 
      self.stream_block = block_types() 
     else: 
      self.stream_block = StreamBlock(block_types) 
     super(StreamField, self).__init__(**kwargs) 

wagtailcore>ブロック> stream_block.py

class StreamBlock(six.with_metaclass(DeclarativeSubBlocksMetaclass, BaseStreamBlock)): 
    pass 

six.py

def with_metaclass(meta, *bases): 
    """Create a base class with a metaclass.""" 
    # This requires a bit of explanation: the basic idea is to make a dummy 
    # metaclass for one level of class instantiation that replaces itself with 
    # the actual metaclass. 
    class metaclass(meta): 

     def __new__(cls, name, this_bases, d): 
      return meta(name, bases, d) 
    return type.__new__(metaclass, 'temporary_class',(), {}) 

QUESTION

(1)の説明は、我々が実際のメタクラスで自身を置き換える一時ダミーメタクラスを生成することを示唆しています。 (2)これはどのように機能しますか? (3)with_metaclass関数を使ってメタクラス生成をどのように順序付けますか? (4)そして、どこのBaseStreamBlockが入りますか?

[2]我々は[で定義されたクラス メタクラスをインスタンス化して
[2] return type.__new__(metaclass, 'temporary_class',(), {}) 

私を混乱させる部分があるが、我々は

[1] class metaclass(meta): 

を定義するだけ経由して、それを呼び出すことです1]。このクラスのインスタンスには、型としてDeclarativeSubBlockMetaclassが含まれています。'temporary_class'は、ベースまたは属性のない名前です。

[1]では、実際のメタクラス作業を行っているように見えるメタクラスクラスを定義しています。ここでは、基底と名前に基づいて型DeclarativeSubBlockMetaclass(メタとして渡される)型のクラスを生成するクラスジェネレータを開発します。

しかし、にのみ呼び出しは、[1] [2]私たちがやっているように見えるすべてが任意の塩基または属性なしタイプDeclarativeSubBlockMetaclassの「temporary_class」をインスタンス化することですから来ているので。

説明(1)で説明したように、この一時的なダミーメタクラスを実際のメタクラスに置き換えるにはどうすればよいですか?

私はこの6つのドキュメントを調べようとしましたが、私の混乱を解消するものは見つかりませんでした。

アドバイスをいただければ幸いです。

はJUSTコンテキストの とてもZ

をありがとう:

私は上記six.with_metaclassコールで使用される2つのクラスのためのコードが含ま:

DeclarativeSubBlocksMetaclass

class DeclarativeSubBlocksMetaclass(BaseBlock): 
    """ 
    Metaclass that collects sub-blocks declared on the base classes. 
    (cheerfully stolen from  https://github.com/django/django/blob/master/django/forms/forms.py) 
    """ 
    def __new__(mcs, name, bases, attrs): 
     # Collect sub-blocks declared on the current class. 
     # These are available on the class as `declared_blocks` 
     current_blocks = [] 
     for key, value in list(attrs.items()): 
      if isinstance(value, Block): 
       current_blocks.append((key, value)) 
       value.set_name(key) 
       attrs.pop(key) 
     current_blocks.sort(key=lambda x: x[1].creation_counter) 
     attrs['declared_blocks'] = collections.OrderedDict(current_blocks) 

     new_class = (super(DeclarativeSubBlocksMetaclass, mcs).__new__(mcs, name, bases, attrs)) 

     # Walk through the MRO, collecting all inherited sub-blocks, to make 
     # the combined `base_blocks`. 
     base_blocks = collections.OrderedDict() 
     for base in reversed(new_class.__mro__): 
      # Collect sub-blocks from base class. 
      if hasattr(base, 'declared_blocks'): 
       base_blocks.update(base.declared_blocks) 

      # Field shadowing. 
      for attr, value in base.__dict__.items(): 
       if value is None and attr in base_blocks: 
        base_blocks.pop(attr) 
     new_class.base_blocks = base_blocks 

     return new_class 

BaseStreamBlock

class BaseStreamBlock(Block): 

    def __init__(self, local_blocks=None, **kwargs): 
     self._constructor_kwargs = kwargs 

     super(BaseStreamBlock, self).__init__(**kwargs) 

     # create a local (shallow) copy of base_blocks so that it can be supplemented by local_blocks 
     self.child_blocks = self.base_blocks.copy() 
     if local_blocks: 
      for name, block in local_blocks: 
       block.set_name(name) 
       self.child_blocks[name] = block 

     self.dependencies = self.child_blocks.values() 

答えて

2

[OK]を - 私はそれを考え出したと思います。問題の核心は、with_metaclass機能の

return meta(name, bases, d) 

である:ここで

def with_metaclass(meta, *bases): 
    """Create a base class with a metaclass.""" 
    # This requires a bit of explanation: the basic idea is to make a dummy 
    # metaclass for one level of class instantiation that replaces itself with 
    # the actual metaclass. 
    class metaclass(meta): 

     def __new__(cls, name, this_bases, d): 
      return meta(name, bases, d) 
    return type.__new__(metaclass, 'temporary_class',(), {}) 

が、私はそれがsudoのコードで動作どのように考えるかです:

(1) with_metaclass takes <<DeclarativeSubBlocksMetaclass>> as meta; and <<BaseStreamBlock>> as bases 
(2) class metaclass(meta) --> the class metaclass is then created extending <<DeclarativeSubBlockMetaclass>> as the class type 
(3) def __new__(cls, name, this_bases, d): Only rarely will you have to worry about __new__. Usually, you'll just define __init__ and let the default __new__ pass the constructor arguments to it. __new__ takes care of creating the object and assigning memory space to it. This __new__ method is a class method that gets called when you create an instance of the class and it gets called before __init__. Its main job is to allocate the memory that the object that you are creating uses. It can also be used to set up any aspect of the instance of the class that is immutable Because classes are kind of immutable (they cannot be changed), overloading __new_ is the best place to overload how they are created. 
(4) return meta(name, bases, d) --> the class definition ends with returning a <<DeclarativeSubBlockMetaclass>> with the arguments (name, base = BaseStreamBlock, d) 

NOTE: We only define the class in 1 - 3; we are not instantiating it this comes below 

(5) return type.__new__(metaclass, 'temporary_class',(), {}) --> Here we are using the classic metaclass syntax. This syntax usually looks like this: return type.__new__(cls, name, bases, attrs). We are using this syntax to instantiate the metaclass we defined in (3) and (4). One might think that it is confusing that temporary_class',(), {} are passed on as the 'name', 'bases', and 'attrs' arguments. BUT... 
(6) ... when the instantiation arrives at return meta(name,bases,d) we notice that meta doesn't take 'this_bases' as an argument but 'bases'. It derives this value from the arguments which were passed to (1) with_metaclasses. As such bases in this instance == <<BaseStreamBlock>> 
(7) Therefore, when we instantiate type.__new__(metaclass, 'temporary_class',(), {}) we essentially execute <<DeclarativeSubBlocksMetaClass>>('temporary_class', <<BaseStreamBlock>>, {}) 

ステップは、(で説明しました7)は、その説明が語ったことです。本質的にSIXが行うことは、temporary_classを呼び出すダミーのメタクラスを作成するための規定された手順を実行することです。 DeclarativeSubBlocksMetaClassもメタクラスであるため、BaseStreamBlockベースを使用して新しいクラスを生成します。

これは意味があると思います。

z

+1

6つのコードよりも答えを理解するのに時間がかかりました。うん、それは難しいですが、あなたはそれの核心を得ます:作成される一時的なメタクラスは決して実際にはインスタンス化されません:その '__new__'メソッドは代わりに元のメタクラスをインスタンス化します。 – jsbueno

関連する問題