元类和__slots__?

时间:2012-03-11 10:23:18

标签: python

所以,我正在阅读Python中的元类,以及type()的三参数alter-ego如何用于动态创建类。但是,第三个参数通常是dict,用于初始化要创建的类'__dict__变量。

如果我想基于使用__slots__而不是__dict__的元类动态创建类,我该怎么做? type()是否仍以某种方式使用以及覆盖__new__()

作为一个FYI,我知道__slots__的正确用途,在创建大量类时节省内存而不是滥用它来强制执行类型安全。


设置__metaclass__并使用__dict__的普通(新式)类的示例:

class Meta(type):
    def __new__(cls, name, bases, dctn):
        # Do something unique ...
        return type.__new__(cls, name, bases, dctn)

class Foo(object):
    __metaclass__ = Meta

    def __init__(self):
        pass

在上文中,type.__new__()被调用,第四个参数(实际使用后成为第三个参数)在__dict__中创建Foo。但是,如果我想修改Meta以包含__slots__,那么我没有字典可以传递给type()的{​​{1}}函数(据我所知 - 我还没有测试任何这个,只是思考并试图找到某种用例场景。)

编辑:快速但未经测试的猜测是将值放入__new__()变量并将其传递给__slots__。然后将type.__new__()添加到__init__(),填充dict中的Meta变量。虽然,我不确定该字典将如何到达__slots__,因为__init__()的声明会阻止创建__slots__,除非在__dict__中定义__dict__。 ..

3 个答案:

答案 0 :(得分:22)

您无法创建具有非空__slots__属性的类型。您可以做的是将__slots__属性插入到新类的dict中,如下所示:

class Meta(type): 
    def __new__(cls, name, bases, dctn):
         dctn['__slots__'] = ( 'x', )
         return type.__new__(cls, name, bases, dctn)

 class Foo(object):
    __metaclass__ = Meta

    def __init__(self):
        pass 

现在Foo有了一些属性:

foo = Foo() 
foo.y = 1

引发

 AttributeError: 'Foo' object has no attribute 'y'

答案 1 :(得分:6)

您的元类示例中的

dctn字典,而不是实例字典。 __slots__替换实例字典。如果您创建两个示例:

class Meta(type):
    def __new__(cls, name, bases, dctn):
        return type.__new__(cls, name, bases, dctn)

class Foo1(object):
    __metaclass__ = Meta

class Foo2(object):
    __metaclass__ = Meta
    __slots__ = ['a', 'b']

然后:

>>> f1 = Foo1()
>>> f2 = Foo2()
>>> f1.__dict__ is Foo1.__dict__
False
>>> f2.__dict__
Traceback (most recent call last):
    ...
AttributeError: 'Foo2' object has no attribute '__dict__'

答案 2 :(得分:2)

有关在元类中定义的插槽的更多信息。

如果type的子类定义非空__slots__,由于某些complicated implementation-related stuff,Python会抛出TypeError

In [1]:

class Meta(type):
    __slots__ = ('x')

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-38-1b16edef8eca> in <module>()
----> 1 class Meta(type):
      2     __slots__ = ('x')

TypeError: nonempty __slots__ not supported for subtype of 'type'
另一方面,

__slots__不会产生任何错误,但接缝无效。

In [2]:

class Meta(type):
    __slots__ = ()

class Foo(metaclass=Meta):
    pass

type(Foo)

Out [2]:

__main__.Meta

In [3]:

Foo.y = 42
Foo.y

Out [3]:

42

In [4]:

Foo.__dict__

Out [4]:

mappingproxy({'y': 42, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__module__': '__main__'})

In [5]:

foo = Meta('foo', (), {})
type(foo).__slots__

Out [5]:

()

In [6]:

foo.x = 42
foo.x

Out [6]:

42

In [7]:

foo.__dict__

Out [7]:

mappingproxy({'__dict__': <attribute '__dict__' of 'foo' objects>, 'x': 42, '__module__': '__main__', '__doc__': None, '__weakref__': <attribute '__weakref__' of 'foo' objects>})

In [8]:

# Testing on non-metaclasses. Just in case.
class Bar:
    __slots__ = ()

b = Bar()
b.__dict__

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-54-843730c66f3f> in <module>()
      3 
      4 b = Bar()
----> 5 b.__dict__

AttributeError: 'Bar' object has no attribute '__dict__'