2

I want to create an abstract class such that subclasses will raise an error when instanced if they don't implement an abstract property. An error should be also raised if the attribute is a method rather than a property. My attempt:

from abc import ABC, abstractmethod
class IClass(ABC):
    @property
    @abstractmethod
    def attr(self):
        pass

Unfortunately, this does nothing to prevent instantiating subclasses with an attr method rather than a property.

The solution should produce the following results:

class C(IClass):
    pass
C()  # Must fail because 'attr' has not been implemented

class C(IClass):
    def attr(self):
        pass
C().attr  # Must fail because attribute 'attr' is a method rather than a property

class C(IClass):
    attr = 'attr'
C().attr  # Must pass because 'attr' is a property

The manual: docs.

Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
MLguy
  • 1,776
  • 3
  • 15
  • 28

1 Answers1

0

This is not as convenient as a decorator, but you can validate that the attribute is a property in __new__ like:

Code:

from abc import ABC, abstractmethod

class IClass(ABC):
    def __new__(cls, *args, **kwargs):
        new = super().__new__(cls, *args, **kwargs)
        if not isinstance(type(new).attr, property):
            raise TypeError('attr must be a property')
        return new

    @property
    @abstractmethod
    def attr(self):
        pass

Test Code:

class A(IClass):
    pass
try:
    # Must fail because 'attr' has not been implemented
    A()
except Exception as exc:
    print('A Passed: {}'.format(exc))

class B(IClass):
    def attr(self):
        pass
try:
    # Must fail because attribute 'attr' is a method rather than a property
    B()
except Exception as exc:
    print('B Passed: {}'.format(exc))

class C(IClass):
    @property
    def attr(self):
        return 'Good'

# Must pass because 'attr' is a property
print('C Passed: {}'.format(C().attr))

Results:

A Passed: Can't instantiate abstract class A with abstract methods attr
B Passed: attr must be a property
C Passed: Good
Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135