6

My problem is as follows: I want to create a class that inherits from typing.NamedTuple and another mixin which in an abstract class. Ideally I want to do something like this:

from typing import *
from abc import ABC, abstractmethod

class M(ABC):
    @abstractmethod
    def m(self, it: Iterable[str]) -> str:
        pass

class N(NamedTuple, M):
    attr1: str

    def m(self, it):
        return self.attr1 + it

When I try to do that now I get this error:

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

I know that I can do it this way:

from typing import *
from abc import ABC, abstractmethod

class M(ABC):
    @abstractmethod
    def m(self, it: Iterable[str]) -> str:
        pass

class NT(NamedTuple):
    attr1: str

class N(NT, M):
    def m(self, it):
        return self.attr1 + it

But I don't want to have to do that because it seems kinda gross and defines 2x the number of classes I am actually going to use. I am also looking for a solution that ideally changes M in some way, not something that I have to specify when creating N everytime.

hchw
  • 1,388
  • 8
  • 14

1 Answers1

5

You need to define a combined metaclass. In this case it's sufficient to make it the metaclass of M

from typing import *
from abc import ABCMeta, abstractmethod

class NamedTupleABCMeta(ABCMeta, NamedTupleMeta):
    pass

class M(metaclass=NamedTupleABCMeta):
    @abstractmethod
    def m(self, it: Iterable[str]) -> str:
        pass

class N(NamedTuple, M):
    attr1: str
    def m(self, it):
        return self.attr1 + it
Patrick Haugh
  • 59,226
  • 13
  • 88
  • 96
  • thanks for the response. almost perfect.... but what if I want to define another attribute on M that I don't want to have to redefine everytime. Right now if I define a method `m1` on Class M, and dont define on Class N, if i make an object from Class N it wont let me use method `m1`... – hchw Aug 15 '18 at 16:53
  • 1
    [See this question](https://stackoverflow.com/questions/50367661/customizing-typing-namedtuple). Basically, `NamedTupleMeta` doesn't play nice with inheritance. One of the reasons Python 3.7 introduced `dataclasses` was to make hierarchies like this more feasible. – Patrick Haugh Aug 15 '18 at 17:10
  • thanks. unfortunately stuck in python 3.6 for various reasons, but good to know. – hchw Aug 15 '18 at 17:14