Python abstract decoration doesn't work

I encoded a decoration (for the sake of curiosity) to create an abstract class in python. It still seemed like this would work, but I got unexpected behavior.

The design idea looks like this:

from abc import ABCMeta, abstractmethod def abstract(cls): cls.__metaclass__ = ABCMeta return cls 

Then, when using this decoration, it was only necessary to determine the abstract method

 @abstract class Dog(object): @abstractmethod def bark(self): pass 

But when I test, I was able to instantiate the Dog object:

 d = Dog() d.bark() //no errors Dog.__metaclass__ //returned "<class 'abc.ABCMeta'>" 

When directly testing the __metaclass__ it behaves as expected:

 class Dog(object): __metaclass__ = ABCMeta @abstractmethod def bark(self): pass 

Testing:

 d = Dog() "Traceback (most recent call last): File "<pyshell#98>", line 1, in <module> d = Dog() TypeError: Can't instantiate abstract class Dog with abstract methods bark" 

Why is this happening?

+7
python abstract-class
source share
2 answers

I was able to reproduce your problem.

ABCMeta .__ new__ is never called using your decorator. When you really define it as a dog metaclass, it is invoked. This is because the Dog class is already defined, so adding ABCMeta after that does not really matter, since its new one , not called during the definition, will not register the Dog class as abstract and will not detect its abstract methods. I invite you to read the ABCMeta code. new and you will see what he does.

However, I found this solution to make your decorator work:

 def abstract(cls): return ABCMeta(cls.__name__, cls.__bases__, dict(cls.__dict__)) 

Now it works as expected. This means that you must subclass Dog in order to be able to call bark .

+2
source share

Link to python state :

When a class definition is read, if __metaclass__ defined, then the caller assigned to it will be called instead of type (). This allows you to write classes or functions that control or modify the process of creating a class.

The important part is that the definition of the class is read , which means that you cannot change the metaclass after that, and this is what the decorator is trying to do, since it is just syntactic sugar.

 @some_decorator class SomeClass(object): pass 

matches with:

 class SomeClass(object): pass SomeClass = some_decorator(SomeClass) 

So, when the decorator is called, the class definition is already read.

+4
source share

All Articles