No, unfortunately there is no way to do anything with the __slots__
after the class is created (and that's when the decorators on them are called). The only way is to use a metaclass, and modify/add __slots__
before calling the type.__new__
.
An example of such a metaclass:
class MetaA(type):
def __new__(mcls, name, bases, dct):
slots = set(dct.get('__slots__', ()))
slots.add('y')
dct['__slots__'] = tuple(slots)
return super().__new__(mcls, name, bases, dct)
class BaseA(metaclass=MetaA):
pass
class A(BaseA):
__slots__ = ('x',)
def __init__(self):
self.x = 1
self.y = 2
print(A().x, A().y)
Without metaclasses, you can do some magic and copy everything from the defined class and create a new one on the fly, but that code smells ;)
def decorator(cls):
slots = set(cls.__slots__)
slots.add('y')
dct = cls.__dict__.copy()
for name in cls.__slots__:
dct.pop(name)
dct['__slots__'] = tuple(slots)
return type(cls)(cls.__name__, cls.__bases__, dct)
@decorator
class A:
__slots__ = ('x',)
def __init__(self):
self.x = self.y = 42
print(A().x, A().y)
The main disadvantage of such code, is that if someone applies another decorator, before yours one, and, let's say, creates a reference to the decorated class somewhere, then they will end up storing reference to a different class. Same for metaclasses - they will execute twice. So, the metaclass approach is better, since there are no side-effects.
The definitive answer of why you can't really change __slots__
after the class is created depends on implementation details of the python interpreter you're working with. For instance, in CPython, for each slot you defined, class has a descriptor (see PyMemberDescr_Type
& PyMemberDef
struct in CPython source code), that has an offset parameter of where the slot value is aligned in internal object storage. And you simply have no instruments of manipulating such things in public Python API. You trade flexibility for less memory usage (again, in CPython, as in PyPy you get the same memory effect automatically for all your classes).
If modification of __slots__
is absolutely required, you can, probably, write a C extension (or work with ctypes
) and do it, but that's hardly a reliable solution.