Подтвердить что ты не робот

Метаклассы и __slots__?

Итак, я немного читаю о метаклассах в Python и как type() three-argument 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() __new__() (насколько мне известно - я еще не тестировал это, просто размышляя и пытаясь найти какой-то сценарий использования).

Изменить: Быстрое, но непроверенное предположение состоит в том, чтобы взять значение значений, которые нужно поместить в переменные __slots__, и передать его в type.__new__(). Затем добавьте __init__() в Meta, который заполняет переменные __slots__ из dict. Хотя я не уверен, как этот dict достигнет __init__(), потому что объявление __slots__ предотвращает создание __dict__, если __dict__ не определено в __slots__...

4b9b3361

Ответ 1

Вы не можете создать тип с непустым атрибутом __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'

Ответ 2

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__'

Ответ 3

Подробнее о слотах, определенных в метаклассах.

Если подкласс type определяет непустую __slots__, 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'

Empty __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__'