# Meta-class to allow inspection of all __slots__of a class

If classes provide __slots__ and at the same time inherit from another class also providing __slots__, there is no straightforward way to access and list each and every slot of the class. Especially when you consider chaining of inheritance.

I've written this meta-class that adds the __all_slots__ property, listing all unique slots:

class MetaSlotMachine(type):
"""Meta-class that adds the attribute __all_slots__ to a class.

__all_slots__  is a set that contains all unique slots of a class,
including the ones that are inherited from parents.

"""
def __init__(cls, name, bases, dictionary):
super(MetaSlotMachine, cls).__init__(name, bases, dictionary)
slots_iterator = (getattr(c, '__slots__', ()) for c in cls.__mro__)
# __slots__ might only be a single string,
# so we need to put the strings into a tuple.
# basestring becomes just str in Python 3
slots_converted = ((slots,) if isinstance(slots, basestring) else slots
for slots in slots_iterator)
cls.__all_slots__ = set()
cls.__all_slots__.update(*slots_converted)


Let's consider classes that inherit slots, like the following:

class HasSlots1(object):
__metaclass__ = MetaSlotMachine   # Python 2 Syntax
__slots__ = ['x', 'y']

class HasSlots2(HasSlots1):
__slots__ = 'zz'

class HasSlots3(HasSlots2):
__slots__ = ()


If we create and instance via

myslots = HasSlots3()


myslots.__slots__


Python will return the empty tuple:

 ()


However, now we can get hold of all slots via

myslots.__all_slots__


which returns all inherited slots:

{'y', 'x', 'zz'}


Is there anything flawed with the design of the meta-class? What about listing all slots based on the __mro__ in _get_all_slots function?

EDIT: Revised version with gathering slots within the init function. Moreover, special care is taken about __slots__ that only consist of a single string, like __slots__ = '__weakref__'.

• Your edit invalidated Gareth Rees' answer, so was against the rules. See For an iterative review, is it okay to edit my own question to include revised code?. However, the answer is deleted so I'm not sure now what the acceptable process is. – Veedrac Apr 2 '15 at 11:23
• That was actually the reason why I edited it, otherwise his suggestions and what I found out by discussing with him would have been lost. – SmCaterpillar Apr 2 '15 at 12:00
• I deleted my answer because it was no longer relevant. – Gareth Rees Apr 2 '15 at 12:42

This is a clever use of metaclasses - in a good way. The only thing I would change is to use set().union over update (this is debatable).
• Why would one prefer mro() over __mro__? From the docs (docs.python.org/2/library/stdtypes.html#class.__mro__) I would assume that mro() is only supposed to be called once at class instantiation (to actually put the resolution order into __mro__). – SmCaterpillar Apr 3 '15 at 8:20
• @SmCaterpillar Ack, that's the darndest thing. Every other __xxx__/xxx() pair exists with the dunder method as the overloadable private version, but with mro the dunder attribute is a cache. Dastardly indeed. That said, CPython does use mro() itself (though less often than __mro__) so neither seems to be more "correct". I guess inspect.getmro, which just returns cls.__mro__, might be the "canonical" solution, but nobody seems to know about it... – Veedrac Apr 3 '15 at 16:02