哇 six.with_metaclass() 有效吗?

Wow does six.with_metaclass() work?

嗨 Whosebug 社区

我一直在努力了解 Django(和 Wagtail 的 Stream-field)在幕后是如何工作的。通过这样做,我了解了 metaclasses 并相信掌握了原理。也就是说,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', (), {})

问题

(1) 描述表明我们生成了一个临时的虚拟元class,它用实际的元class 替换了自己。 (2) 这是如何运作的? (3) 我们如何通过 with_metaclass 函数对元 class 生成进行排序? (4) BaseStreamBlock 是从哪里来的?

让我感到困惑的部分是我们定义

[1] class metaclass(meta):

但只能通过以下方式调用:

[2] return type.__new__(metaclass, 'temporary_class', (), {})

在[2]中我们实例化了我们在[1]中定义的classmetaclass。这个 class 的实例包含 DeclarativeSubBlockMetaclass 作为类型,'temporary_class' 作为没有基础或属性的名称。

在 [1] 中,我们定义了 metaclass class,它似乎在执行实际的 metaclass 工作。在这里,我们开发了一个 class 生成器,它根据基数和名称生成 class 类型为 DeclarativeSubBlockMetaclass(作为元传入)的 es。

但是,由于对 [1] 的唯一调用来自 [2],我们所做的似乎只是实例化 DeclarativeSubBlockMetaclass[= 类型的 'temporary_class' 78=] 没有任何基础或属性。

我们如何用描述 (1) 中描述的实际元class 替换这个临时虚拟元class?

为此,我尝试查阅 six 的文档,但找不到任何可以解决我困惑的内容。

如有任何建议,我们将不胜感激。

非常感谢 Z

仅供参考:

我包含了上面 six.with_metaclass 调用中使用的两个 classes 的代码:

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()

好的 - 我想我明白了。问题的症结在于

return meta(name, bases, d)
with_metaclass 函数的

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 所做的是通过规定的步骤创建一个虚拟元 class,它称之为 temporary_class。由于 DeclarativeSubBlocksMetaClass 也是一个 metaclass,然后它使用 BaseStreamBlock base 生成一个新的 class.

我希望这是有道理的。

Z