Cannot inherit from multiple classes defining __slots__?

class, python, slots

Solution

By forcing a constraint that no class defines __slots__, a special object class could be constructed with the characteristics desired for all child classes. The class is registered as an alias for regular objects.

class _object: __slots__ = '_MetaSafe__exec', '__dict__'

class MetaSafe(type):

    __REGISTRY = {object: _object}

    @classmethod
    def clone(cls, old):
        return cls(old.__name__, old.__bases__, dict(old.__dict__), old)

    def __new__(cls, name, bases, classdict, old=None):
        # Check on a few classdict keys.
        assert '__new__' not in classdict, '__new__ must not be defined!'
        assert '__slots__' not in classdict, '__slots__ must not be defined!'
        assert '__module__' in classdict, '__module__ must be defined!'
        # Validate all the parent classes.
        valid = []
        for base in bases:
            if base in cls.__REGISTRY:
                valid.append(cls.__REGISTRY[base])
            elif base in cls.__REGISTRY.values():
                valid.append(base)
            else:
                valid.append(cls.clone(base))
        # Wrap callables without thread mark.
        for key, value in classdict.items():
            if callable(value):
                classdict[key] = cls.__wrap(value)
        # Fix classdict and create new class.
        classdict.update({'__new__': cls.__new, '__slots__': (), '__module__':
                          '{}.{}'.format(__name__, classdict['__module__'])})
        cls.__REGISTRY[old] = new = \
            super().__new__(cls, name, tuple(valid), classdict)
        return new

    def __init__(self, name, bases, classdict, old=None):
        return super().__init__(name, bases, classdict)

    @staticmethod
    def __wrap(func):
        @functools.wraps(func)
        def safe(self, *args, **kwargs):
            return self.__exec(func, self, *args, **kwargs)
        return safe

    @classmethod
    def __new(meta, cls, *args, **kwargs):
        self = object.__new__(cls, *args, **kwargs)
        if 'master' in kwargs:
            self.__exec = kwargs['master'].__exec
        else:
            array = tuple(meta.__REGISTRY.values())
            for value in args:
                if isinstance(value, array):
                    self.__exec = value.__exec
                    break
            else:
                self.__exec = Affinity()
        return self

This code can be used as a building block to make `tkinter` thread-safe by cloning its classes. The `Affinity` class automatically ensures that code is executed on a single thread, preventing GUI errors.

Problem

A certain situation in Python recently alarmed me, and its reason is still not completely clear after a little research. The following class definitions appear to work flawlessly and will produce what is intended: ``` class A: __slots__ = 'a', 'b' class B(A): __slots__ = () class C(A): __slots__ = () class D(B, C): __slots__ = () ``` These are four classes arranged in a diamond inheritance pattern. However, a somewhat similar pattern is not allowed. The following class definitions seem as though they should function the same as the first: ``` class B: __slots__ = 'a', 'b' class C: __slots__ = 'a', 'b' class D(B, C): __slots__ = () Traceback (most recent call last): File "<pyshell#74>", line 1, in <module> class D(B, C): __slots__ = () TypeError: multiple bases have instance lay-out conflict ``` However, a `TypeError` is raised in this example. So three questions arise: (1) Is this a bug in Python, considering the slot names? (2) What would justify such an answer? (3) What is the best workaround? References: - __slots__ and multiple inheritance (with replies following) - Multiple inheritance and __slots__ - Don't use __slots__ - Multiple Inheritance __slots__ problem

Original source

Related problems