1

I'm trying to figure out how to ensure that a method of a class inheriting from an ABC is created using the appropriate decorator. I understand (hopefully) how ABCs work in general.

from abc import ABCMeta, abstractmethod

class MyABC(metaclass=ABCMeta):
    @abstractmethod
    def my_abstract_method(self):
        pass

class MyClass(MyABC):
    pass

MyClass()

This gives "TypeError: Can't instantiate abstract class MyClass with abstract methods my_abstract_method". Great, makes sense. Just create a method with that name.

class MyClass(MyABC):
    def my_abstract_method(self):
        pass

MyClass()

Boom. You're done. But what about this case?

from abc import ABCMeta, abstractmethod

class MyABC(metaclass=ABCMeta):    
    @property
    @abstractmethod
    def my_attribute(self):
        pass

class MyClass(MyABC):
    def my_attribute(self):
        pass

MyClass()

The MyClass() call works even though my_attribute is not a property. I guess in the end all ABCs do is ensure that a method with a given name exists. Thats it. If you want more from it, you have to look at MyABC's source code and read the documentation. The decorators and comments there will inform you of how you need to construct your sub-class.

Do I have it right or am I missing something here?

martineau
  • 119,623
  • 25
  • 170
  • 301
  • That's what `abstractproperty` is for. – user2357112 Aug 09 '16 at 02:01
  • ``abstractproperty`` was deprecated in python 3.3. OP has the correct usage for 3.3+; ``@property`` and ``@abstractmethod`` together in the base class, with ``@property`` in the subclass. You can't enforce subclasses to implement it correctly though. – bcb Aug 09 '16 at 02:09

1 Answers1

2

You're correct that ABCs do not enforce that. There isn't a way to enforce something like "has a particular decorator". Decorators are just functions that return objects (e.g., property returns a property object). ABCMeta doesn't do anything to ensure that the defined attributes on the class are anything in particular; it just makes sure they are there. This "works" without errors:

class MyABC(metaclass=ABCMeta):
    @abstractmethod
    def my_abstract_method(self):
        pass

class MyClass(MyABC):
    my_abstract_method = 2

MyClass()

That is, ABCMeta doesn't even ensure that the abstract method as provided on the subclass is a method at all. There just has to be an attribute of some kind with that name,

You could certainly write your own metaclass that does more sophisticated checking to ensure that certain attributes have certain kinds of values, but that's beyond the scope of ABCMeta.

BrenBarn
  • 242,874
  • 37
  • 412
  • 384
  • Thanks. I thought that ABCs would be able to check for a particular decorator exactly because they return class instances (depending on how they are defined). Seems like isinstance() could be used to do the checks there. It's all good. I was trying to think of ways to make my 'static type' fanboy coworkers happy. I read that ABCs can be used to enforce an interface, but that doesn't really seem to be the case. – VagueAdvice Aug 09 '16 at 13:43
  • @VagueAdvice: What I'm saying about decorators is that you can only enforce what type of value the attribute has, not whether that value was created via a decorator or via some other means. (But you can't do that with ABCs anyway.) If you want to satisfy static type fanboys you might want to look at the new `typing` module. – BrenBarn Aug 09 '16 at 16:33
  • I get ya. Thanks for the tip on `typing`. I was joking about calling those guys fanboys. Obviously static typing has huge benefits. We're looking at altering the stack here and I want to push for python. They brought this up as a huge weakness. I was just looking to see what is out there and came across ABCs. Type hinting with mypy as a linter (along with the IDE warnings) might help the cause. – VagueAdvice Aug 09 '16 at 20:34