19

I think the code will explain the problem better than I can do with words. Here is the code in my_abc.py:

from abc import ABCMeta, abstractmethod

class MyABC(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def print(self):
        pass

Here is the code in my_class.py

from my_abc import MyABC
from third_party_package import SomeClass

class MyClass(MyABC, SomeClass):
    def __init__(self):
        super(MyClass, self).__init__()

    def print(self):
        print('Hello ABC')

When I try to run my_class.py I get:

TypeError: Error when calling the metaclass bases metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

I understand that I can create a class the inherits directly from my interface MyABC, and then create another class which then inherits from both this class which I created and my third party module class.

My question is: Is there another better and proper way of doing this directly without having to create an intermediate class for my purpose?

I159
  • 29,741
  • 31
  • 97
  • 132
moeabdol
  • 4,779
  • 6
  • 44
  • 43
  • 1
    Does you suggested workaround actually work? I'd expect you to have exactly the same issue if you put another class in between the `MyABC` and `MyClass`. The issue is that `SomeClass` has a different and incompatible metaclass. Without knowing what that is, I'm not sure if there's any way of answering the question. Can you add some concrete details about `SomeClass`? – Blckknght Mar 01 '15 at 22:05
  • I'm using a third-party library called urwid. It's an ncurses replacement for python which has a much cleaner API. According to the example I provided above the SomeClass corresponds to an urwid.ListBox class which is a widget for creating list-box like UI component. I'm not sure how to find out the metaclass of urwid.ListBox, but when I run it with the code above I get the same TypeError. – moeabdol Mar 02 '15 at 09:59
  • 1
    Try creating an intermediate metaclass: `class MyMeta(ABCMeta, type(SomeClass)): pass`, and then using that as the `__metaclass__` for `MyClass`. – user4815162342 Mar 02 '15 at 15:25

3 Answers3

11

The SomeClass class has a custom metaclass. You will need to create a metaclass which inherits from both ABCMeta and this custom metaclass, then use it as the metaclass for MyClass. Without knowing more about this custom metaclass, I cannot determine a correct way to do this in the general case, but it will probably look like one of these possibilities:

class DerivedMeta(ABCMeta, type(SomeClass)):
    pass

class DerivedMeta(type(SomeClass), ABCMeta):
    pass

It's unlikely but possible you will also need to override one or more methods to ensure correct metaclass interactions.

Kevin
  • 28,963
  • 9
  • 62
  • 81
  • 1
    @moeabdol: There is some discussion of it in [this essay](https://www.python.org/download/releases/2.2/descrintro/#metaclasses), but it talks about a lot of other stuff too. – Kevin Mar 02 '15 at 15:41
  • for the unaware, I have found a great explanation of metaclasses in stackoverflow checkout e-satis's answer here http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python thank you stackoverflow – moeabdol Mar 08 '15 at 00:01
6

Thread is still at the top in search result, so I wanted to share my complete solution.
I ran into this problem when trying to create an abstract template class meant to PyQt5 widgets, in Python 3.8. I applied @Kevin's solution, creating a new meta class first. The working code:

from abc import ABC, ABCMeta
from PyQt5.QtWidgets import QWidget, QLabel


class QABCMeta(ABCMeta, type(QWidget)):
    """Create a meta class that combines ABC and the Qt meta class"""
    pass


class TcWidget(ABC, metaclass=QABCMeta):
    """Abstract class, to be multi-inherited together with a Qt item"""
    pass


class TcLabel(QLabel, TcWidget):
    """Label that shows a value"""
    pass


# ...
label = TcLabel()
# ...
Roberto
  • 958
  • 13
  • 33
0

You could also set the other class's metaclass to ABCMeta so that all the multiple base classes's metaclass are ABCMeta.

I had a similar problem when using multiple inheritance with:

  • a class having a custom metaclass
  • a class having ABCMeta as metaclass

what I did to solve this was make my custom metaclass derive from ABCMeta. This seems to work in all cases.

This worked for me in all cases.

class MyMeta(ABCMeta):
  pass

instead of

class MyMeta(type):
  pass
sanyassh
  • 8,100
  • 13
  • 36
  • 70
g0c
  • 1