当使用用户元类创建 类 时如何将项目添加到 __slots__

How to add items to __slots__ when the classes are created with user metaclass

背景

我想制作一个工具来创建 对象 ,这些对象 没有相同的 属性 。最后我决定创建一个 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'

有什么想法吗?

有点难看,但是你可以在 class 定义语法中将关键字参数传递给 metaclass,比如:

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

元class可以按如下方式处理:

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__ 就可以了,你可以在 metaclass 中删除 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

有效。它会创建您期望的开槽 class。