Как добавить элементы в __slots__, когда классы создаются с метаклассом пользователя - PullRequest
0 голосов
/ 15 октября 2019

Контекст

Я хочу создать инструмент для создания объектов , которые не имеют одинаковых атрибутов . Наконец, я решил создать UniqueInstances metaclass

class UniqueInstances(type):
    def __new__(cls, name, bases, dict):
        dict['instancesAttrs'] = set()

        return super(UniqueInstaces, cls).__new__(cls, name, bases, dict)

    def __call__(cls, *args):
        if args not in cls.instancesAttrs:
            cls.instancesAttrs.add(args)

            return super().__call__(*args)
        else:
            print("Warning: " +
                  "There is another instance of the class " +
                  "'{}' ".format(cls.__name__) +
                  "with the same attributes. The object was not created.")

            return None

С этим metaclass я могу создать классы , которые избегают создания объектов с такими же атрибутами

class Coordinate(metaclass=UniqueInstances):
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z


myObj = Coordinate(0, 0, 0)
myObj = Coordinate(0, 0, 0)  # Warning: There is another instance...

и его работ.

Проблема

Я хочу, чтобы классы, созданные с помощью этого инструмента, имели __slots__ для оптимизации экземпляров. Я могу добавить к dict __slot__ ключ с пустым кортежем

class UniqueInstances(type):
    def __new__(cls, name, bases, dict):
        dict['instancesAttrs'] = set()
        dict['__slots__'] = ()

        return super(UniqueInstaces, cls).__new__(cls, name, bases, dict)

и его работам

class MyClass(metaclass=UniqueInstances):
    pass


myObj = MyClass()
print(myObj.__dict__)  # AttributeError: 'MyClass' object has no attribute '__dict__'

Но я не знаю, как добавлять элементы в__slots__

class Coordinate(metaclass=UniqueInstances):
    __slots__ = ('x', 'y', 'z')

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z


myObj = Coordinate(0, 0, 0)  # AttributeError: 'Coordinate' object has no attribute 'x'

Есть идеи?

1 Ответ

1 голос
/ 15 октября 2019

Немного некрасиво, но вы можете передать ключевые аргументы метаклассу в синтаксисе определения класса, что-то вроде:

class Coordinate(metaclass=UniqueInstances, slots=('x', 'y', 'z')):
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

И метакласс может обрабатывать это следующим образом:

class UniqueInstances(type):
    def __new__(cls, name, bases, dict, **kwargs):
        dict['instancesAttrs'] = set()
        slots = kwargs.get('slots')
        if slots is not None:
            dict['__slots__'] = slots

        return super(UniqueInstances, cls).__new__(cls, name, bases, dict)

РЕДАКТИРОВАТЬ:

На самом деле, я только что проверил и просто с помощью __slots__ работает, вы можете просто удалить dict['__slots__'] = () в метаклассе, так что просто:

class UniqueInstances(type):
    def __new__(cls, name, bases, dict, **kwargs):
        dict['instancesAttrs'] = set()
        return super(UniqueInstances, cls).__new__(cls, name, bases, dict)

class Coordinate(metaclass=UniqueInstances):
    __slots__ = ('x','y','z')
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

Работает. Он создает ожидаемый класс.

...