Multiple sequential abstract base classes in Python 3.4+ - python

Take a common vehicle inheritance example where Vehicle and Car are ABCs and the latter inherits from the former. We then have e.g. FordMustang which is not abstract and should inherit from Car.
So, we have
class Vehicle(ABC):
#abstractmethod
def abstract_vehicle_method(self):
pass
Which is presumably all fine and dandy. However, for the Car class, I am not sure whether it should inherit only from Vehicle or from Vehicle and ABC, and if so, in what order? Moreover, I would like to force abstract_vehicle_method() to be defined in FordMustang and all such non-abstract classes, so should I repeat the abstract_vehicle_method definition in Car or will the inheritance sort this out?
To enumerate the options:
Should it be
class Car(Vehicle):
[...]
or
class Car(ABC, Vehicle):
[...]
or
class Car(Vehicle, ABC):
[...]
and (assuming the first is correct for simplicity) should it be
class Vehicle(ABC):
#abstractmethod
def abstract_vehicle_method(self):
pass
class Car(Vehicle):
#abstractmethod
def abstract_vehicle_method(self):
pass
# Some car specific defs here
class FordMustang(Car):
def abstract_vehicle_method(self):
# Concrete def here
or just
class Vehicle(ABC):
#abstractmethod
def abstract_vehicle_method(self):
pass
class Car(Vehicle):
# Some car specific defs here
class FordMustang(Car):
def abstract_vehicle_method(self):
# Concrete def here

You can directly inherit from Vehicle; the metaclass that makes Vehicle 'abstract' is inherited along with it. You do not need to mix in ABC for those subclasses.
You also don't need to re-define the method. It is inherited too, along with it's 'abstractness'.
Under the hood, the metaclass tracks what attributes are 'abstract'; as long as there are any, the class can't be used to create instances. You can add more abstract methods, or by providing a different attribute for the same name, remove abstract methods from the set.
Demo for your specific example:
>>> from abc import ABC, abstractmethod
>>> class Vehicle(ABC):
... #abstractmethod
... def abstract_vehicle_method(self):
... pass
...
>>> type(Vehicle) # it's an abstract class
<class 'abc.ABCMeta'>
>>> Vehicle.__abstractmethods__ # this set determines what is still abstract
frozenset({'abstract_vehicle_method'})
>>> class Car(Vehicle):
... pass
...
>>> type(Car) # still an abstract class
<class 'abc.ABCMeta'>
>>> Car.__abstractmethods__ # still has abstract methods, incl. inherited methods
frozenset({'abstract_vehicle_method'})
>>> class FordMustang(Car):
... def abstract_vehicle_method(self):
... pass
...
>>> type(FordMustang) # still an abstract class
<class 'abc.ABCMeta'>
>>> FordMustang.__abstractmethods__ # but with no abstract methods left
frozenset()
>>> FordMustang() # so we can create an instance
<__main__.FordMustang object at 0x106054f98>

Related

Why does inheriting dict disable my base class from enforcing a property?

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?
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

Differences in three ways to define a abstract class

I found multiple (slightly different) ways to define abstract classes in Python. I read the documentation and also could not find an answer here on stackoverflow.
The main difference between the three examples (see code below) is:
A sets a new metaclass abc.ABCMeta explicitly
B inherits from abc.ABC
C inherits from objects but defines #abc.abstractmethod classes
It seems that A and B are not different (i.e. also B has the new metaclass abc.ABCMeta). However, class C remains of type type.
What are the impacts of not defining a metaclass for C? When is it necessary to define the metaclass or is it wrong/bad style to not define the abc.ABCMeta metaclass for an abstract class? Nonetheless, the class C seems to behave as I expect from an ABC.
import abc
class A(metaclass=abc.ABCMeta):
# Alternatively put __metaclass__ = abc.ABCMeta here
#abc.abstractmethod
def foo(self):
raise NotImplementedError
class B(abc.ABC):
#abc.abstractmethod
def foo(self):
raise NotImplementedError
class C(object):
#abc.abstractmethod
def foo(self):
raise NotImplementedError
class Aimpl(A):
def foo(self):
print("Aimpl")
class Bimpl(B):
def foo(self):
print("Bimpl")
class Cimpl(C):
#def foo(self):
# print("Cimpl")
pass
Aimpl().foo() # Aimpl
print(isinstance(Aimpl, A)) # False
print(issubclass(Aimpl, A)) # True
print(isinstance(Aimpl, abc.ABCMeta)) # True
print(type(A)) # <class 'abc.ABCMeta'>
print("---")
Bimpl().foo() # Bimpl
print(isinstance(Bimpl, B)) # False
print(issubclass(Bimpl, B)) # True
print(isinstance(Bimpl, abc.ABCMeta)) # True
print(type(B)) # <class 'abc.ABCMeta'>
print("---")
Cimpl().foo() # Cimpl
print(isinstance(Cimpl, C)) # False
print(issubclass(Cimpl, C)) # True
print(isinstance(Cimpl, abc.ABCMeta)) # False
print(type(C)) # <class 'type'>
print("---")
The abc.ABCMeta class is necessary to actually enforce the abstractmethod behaviour. Its itention is to disallow instantiation of any classes which do not implement the abstract method. The decorator itself cannot enforce that, the metaclass is enforcing the decorator upon instantiation:
class Foo:
#abstractmethod
def bar(self):
pass
Foo() # works
However:
class Foo(metaclass=ABCMeta):
#abstractmethod
def bar(self):
pass
Foo()
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: Can't instantiate abstract class Foo with abstract methods bar
So, without the metaclass, the abstractmethod decorator doesn't do anything.
abc.ABC is merely a shorthand so you can do Foo(ABC) instead of Foo(metaclass=ABCMeta), that is all:
A helper class that has ABCMeta as its metaclass. With this class,
an abstract base class can be created by simply deriving from ABC
avoiding sometimes confusing metaclass usage [..]
https://docs.python.org/3/library/abc.html#abc.ABC

Get class name in attribute definition

I need to make a mixin that knows the name of the class who is using it. Sort of like:
class FooMixin(...):
bar = self.__class__
Except that self is not defined at attribute definition time. Is there a clean way to achieve this so it's transparent for the class who inherits the mixin?
At the time of definition of your mixin, nobody knows in which classes your mixin is used. You can only get the name dynamically in class methods by using self.__class__.__name__:
class FooMixin(object):
def some_method(self):
print "I'm in class %s" % self.__class__.__name__
class Main(FooMixin):
pass
instance = Main()
instance.some_method() # "I'm in class Main"
Daniel's answer gives the reason why this is not possible in the declarative way you seem to like it - nobody knows at Mixin's definition-time where and when it will be used.
However, if you don't care about the time, but want the syntax, meaning you want to access bar defined as property in Mixin, and return self.class, this should work:
class classproperty(object):
def __get__(self, instance, clazz):
return clazz
class Mixin(object):
bar = classproperty()
class Foo(Mixin):
pass
print Foo().bar
First off, no special action is needed to know the name of a class:
class MyMixin(object):
def frob(self):
print "frobbing a", self.__class__.__name__
class Foo(MyMixin): pass
class Bar(MyMixin): pass
>>> Foo().frob()
frobbing a Foo
>>> Bar().frob()
frobbing a Bar
similarly, no special action is needed to discover subclasses:
>>> MyMixin.__subclasses__()
[__main__.Foo, __main__.Bar]
If these aren't what you need, because you want to take action when your base class is subclassed, you need a metaclass!:
class MyMixinMeta(type):
def __init__(cls, name, bases, attrs):
if bases != (object,):
print name, cls, "is a subclass of", bases
class MyMixin(object):
__metaclass__ = MyMixinMeta
def frob(self):
print "frobbing a", self.__class__.__name__
>>> class Foo(MyMixin): pass
Foo <class '__main__.Foo'> is a subclass of (<class '__main__.MyMixin'>,)
>>> class Bar(MyMixin): pass
Bar <class '__main__.Bar'> is a subclass of (<class '__main__.MyMixin'>,)

get list of child class in python [duplicate]

I was hoping to make a list of all subclasses of a given class by having each subclass register itself in a list that a parent class holds, ie something like this:
class Monster(object):
monsters = list()
class Lochness(Monster):
Monster.monsters.append(Lochness)
class Yeti(Monster):
Monster.monsters.append(Yeti)
This obviously doesn't work because the classes haven't been created yet when I want to add them to the list. And, it'd be much nicer if it were done automatically (like __subclass__)
I'm aware that __subclass__ has this functionality, but I was wondering (for my own edification) how you'd implement it yourself.
It seems like you'd want to create some sort of subclass of the metaclass which is creating everything to register it with Monster? Or is that completely off base
Classes already register what subclasses are defined; call the class.__subclasses__() method to get a list:
>>> class Monster(object):
... pass
...
>>> class Lochness(Monster):
... pass
...
>>> class Yeti(Monster):
... pass
...
>>> Monster.__subclasses__()
[<class '__main__.Lochness'>, <class '__main__.Yeti'>]
.__subclasses__() returns a list of currently still alive subclasses. If you ever would clear all references to Yeti (del Yeti in the module, delete all instances, subclasses, imports, etc.) then it'd no longer be listed when you call .__subclasses__(). Note that in essence, .__subclasses__() is a CPython implementation detail, but the method is present in all Python versions that support new-style classes (2.2 and up, all the way to 3.x).
Otherwise, the canonical way to hook into class creation is to define a metaclass:
class MonstersMeta(type):
def __new__(metaclass, name, bases, namespace):
cls = super(MonstersMeta, metaclass).__new__(metaclass, name, bases, namespace)
if issubclass(cls, Monster) and not cls is Monster:
Monster.monsters.append(cls)
return cls
class Monster(object):
__metaclass__ = MonstersMeta
monsters = []
class Lochness(Monster):
pass
class Yeti(Monster):
pass
Demo:
>>> class Monster(object):
... __metaclass__ = MonstersMeta
... monsters = []
...
>>> class Lochness(Monster):
... pass
...
>>> class Yeti(Monster):
... pass
...
>>> Monster.monsters
[<class '__main__.Lochness'>, <class '__main__.Yeti'>]
or you can use a class decorator:
def registered_monster(cls):
Monster.monsters.append(cls)
return cls
class Monster(object):
monsters = []
#registered_monster
class Lochness(Monster):
pass
#registered_monster
class Yeti(Monster):
pass
Demo:
>>> class Monster(object):
... monsters = []
...
>>> #registered_monster
... class Lochness(Monster):
... pass
...
>>> #registered_monster
... class Yeti(Monster):
... pass
...
>>> Monster.monsters
[<class '__main__.Lochness'>, <class '__main__.Yeti'>]
The difference being where you put the responsibility of registering monsters; with the base MonstersMeta type, or with explicit decorators.
Either way, the metaclass or the class decorator registers a permanent reference. You can use the weakref module if you really, really want to emulate the .__subclasses__() behaviour.
Except from the obvious solution for this case to use type.__subclasses__(), you could use a decorator for similar problems:
class Monster(object):
monsters = list()
def isMonster(cls):
Monster.monsters.append(cls)
return cls
#isMonster
class Lochness(Monster):
pass
#isMonster
class Yeti(Monster):
pass
print(Monster.monsters) # [<class '__main__.Lochness'>, <class '__main__.Yeti'>]
Just to keep your code, add classes outside their definition:
class Monster(object):
monsters = list()
class Lochness(Monster):
pass
Monster.monsters.append(Lochness)
class Yeti(Monster):
pass
Monster.monsters.append(Yeti)
But, as said: if this is a common feature, create a Metaclass

Question about python and classes

I have a base class and a few derived in Python:
class Base:
def Foo(self):
pass
# First derived class
class Der1(Base):
def OwnFoo(self):
# Do something 1
def OwnFoo2(self):
# Do something 2
def Foo(self):
# Do something 3
# Second derived class
class Der2(Base):
def OwnFoo(self):
# Do something 1
def OwnFoo2(self):
# Do something 2
def Foo(self):
# Do something 3
The question is:
I have some predefined code in Der1. Almost all functions from Der2 do the same. How can I write this with less code?
I can't add that code to the parent. Parent class shouldn't be touched.
For example, Der2.OwnFoo does the same as Der1.OwnFoo, maybe there is some construction in python just to call OwnFoo from first class and not to write that code again?
I can't change the parent of Der1 and Der2! It should be Base.
Since you can't change the inheritance structure, make a helper class that contains the common code and include it by composition rather than inheritance.
# Common code goes in this new class
class DerHelper:
def __init__(self, parent):
self._parent = parent
def OwnFoo(self):
print 'Do something 1', self._parent
def OwnFoo2(self):
print 'Do something 2', self._parent
def Foo(self):
print 'Do something 3', self._parent
# First derived class
class Der1(Base):
def __init__(self):
# include helper class by composition
self._helper = DerHelper('Der1')
def OwnFoo(self):
self._helper.OwnFoo()
def OwnFoo2(self):
self._helper.OwnFoo2()
def Foo(self):
self._helper.Foo()
# Second derived class
class Der2(Base):
def __init__(self):
# include helper class by composition
self._helper = DerHelper('Der2')
def OwnFoo(self):
self._helper.OwnFoo()
def OwnFoo2(self):
self._helper.OwnFoo2()
def Foo(self):
self._helper.Foo()
Of course, you could pass a reference to the parent instead of a string. I just did it this way for demonstration purposes.
Usage:
d = Der1()
d.OwnFoo()
d.OwnFoo2()
d.Foo()
d = Der2()
d.OwnFoo()
d.OwnFoo2()
d.Foo()
Output:
Do something 1 Der1
Do something 2 Der1
Do something 3 Der1
Do something 1 Der2
Do something 2 Der2
Do something 3 Der2
Make Der2 a subclass of Der1 and you're done.
class Base:
def Foo(self):
pass
# First derived class
class Der1(Base):
def OwnFoo(self):
# Do something 1
def OwnFoo2(self):
# Do something 2
def Foo(self):
# Do something 3
# Second derived class (subclasses Der1)
class Der2(Der1):
pass
Any behavior within Der2 you'd like to specialize can added within the class definition. If you create a new method of the same name in Der2 (e.g. Der2.OwnFoo()), then it will overload the default method that is inherited from Der1.
EDIT: If you can't change the parent, put all of the behavior you want to inherit in the base class keeping in mind that you can overload or customize any of the methods in the subclasses.
In code:
# Base class
class Base:
def Foo1(self):
# Do something 1
def Foo2(self):
# Do something 2
def Foo(self):
# Do something 3
# First derived class, inherits everything from Base
class Der1(Base):
pass
# Second derived class
class Der2(Base):
pass
There is a "trick" you can do to call the original method inherited from the parent, capture the return value and then customize the behavior. This will only work if the method actually returns a value, and can be dangerous if the method manipulates attributes within the class, unless that's what you want and expect it.
In code:
# Second derived class, with customized methods
class Der2(Base):
# Anything that is not explicitly define is inherited from parent
# as-is.
def Foo(self):
# My Foo() overloads Base.Foo() inherited from parent class.
# Do something "special" 3
def Foo1(self):
# Calls parent Base.Foo1() and then manipulates return data.
base_output = Base.Foo1(self)
# Do something "special" 1 with 'base_output'
Is this a homework?
Look at the first line of Der2:
class Der2(Base):
What says what is its parent (e.g. a class it descends and herits methods and attributes from)? How could you change this?
If Der1 and Der2 share a lot of code, then you should put that in a superclass; since Base cannot be touched, introduce a class in between:
class Der(Base):
def OwnFoo(self):
...
class Der1(Der):
...
class Der2(Der):
...
(Depending on you class hierachy, the "derive Der2 from Der1" option that others recommend may also be valid.)
How about making Der2 subclass Der1?

Categories