1

Here I have a class hierarchy, where I want to enforce all subclasses of AnimalWithFur to define a property of fur_type:

from abc import ABC, abstractmethod

class Animal:
    ...


class AnimalWithFur(ABC, Animal):
    @property
    @abstractmethod
    def fur_type(self) -> str:
        ...


class Dog(AnimalWithFur):
        ...


dog = Dog()
print(dog.fur_type)

This works fine. When I try to instantiate dog, it will raise the expected exception.

But, let's say I want to spice things up and make AnimalWithFur a dict instead of an Animal:

from abc import ABC, abstractmethod

class Animal:
    ...


class AnimalWithFur(ABC, dict):
    @property
    @abstractmethod
    def fur_type(self) -> str:
        ...


class Dog(AnimalWithFur):
        ...


dog = Dog()
print(dog.fur_type)

This code no longer works (it does not throw an exception that Dog() hasn't defined fur_type anymore...)

Why doesn't it work, and what can I do to fix it?

Abraham
  • 93
  • 5
  • If it helps, it looks like `Animal` and `property` are beside the point; you can reproduce the problem without them – wjandrea May 20 '22 at 17:52
  • I don't know why it's happening, but for fixing it, look into existing questions about the problems of inheriting from built-in types, like [Subclass dict: UserDict, dict or ABC?](/q/7148419/4518341) and [How to "perfectly" override a dict?](/q/3387691/4518341) I tried swapping out `dict` for `UserDict` and it worked as expected. – wjandrea May 20 '22 at 18:18
  • 2
    Some discussion on this issue here - looks like no resolution so far: https://github.com/python/cpython/issues/50246 – aganders3 May 20 '22 at 18:18

1 Answers1

0

As mentionied in a comment above, this has been an issue for some time with not much movement on a solution. Basically the issue is present when inheriting from builtin types. See https://github.com/python/cpython/issues/50246 for more info.

As a solution, consider inheriting from UserDict instead:

from abc import ABC
from collections.abc import UserDict


class AnimalWithFur(ABC, UserDict):
    @property
    @abstractmethod
    def fur_type(self) -> str:
        ...


class Dog(AnimalWithFur):
        ...


dog = Dog()
# the above line will fail with the following TypeError
# TypeError: Can't instantiate abstract class Dog with abstract method fur_type
aganders3
  • 5,838
  • 26
  • 30