19

Is there a way to declare an abstract instance variable for a class in python?

For example, we have an abstract base class, Bird, with an abstract method fly implemented using the abc package, and the abstract instance variable feathers (what I'm looking for) implemented as a property.

from abc import ABCMeta, abstractmethod

class Bird(metaclass=ABCMeta):

    @property
    @abstractmethod
    def feathers(self):
        """The bird's feathers."""

    @abstractmethod
    def fly(self):
        """Take flight."""

The problem is that Eagle, a class derived from Bird, is required to have feathers implemented as a property method. So the following is not an acceptable class, but I'd like it to be

class Eagle(Bird):

    def __init__(self):
        self.feathers = 'quill'

    def fly(self):
        print('boy are my arms tired')

There might be a problem since the requirement is on the instance itself, and really after its instantiation, so I don't know if things like the abc package will still work.

Are there some standard ways of handling this?

Jay Calamari
  • 573
  • 4
  • 17
  • Maybe I missed the point. Can you shortly explain why do you need an abstract property? Otherwise just let it be a normal property. – rbf Jun 27 '18 at 06:31
  • I'd like it to be enforced, so that if Eagle instances don't have a `feathers` attribute, a helpful error is raised (to avoid unpredictable behavior). (If you meant more specifically, sorry but it's hard to answer, since I was hoping to use this for several different variables.) – Jay Calamari Jun 27 '18 at 06:34

2 Answers2

9

Something simple like the following can work, using a common property:

class Bird(object):
    @property
    def feathers(self):
        try:
            return self._feathers
        except AttributeError:
            raise WhateverError('No feathers')  # maybe obfuscate inner structure

class Eagle(Bird):
    def __init__(self):
        self._feathers = 'quill'

>>> Bird().feathers
WhateverError: No feathers
>>> Eagle().feathers
'quill'
user2390182
  • 72,016
  • 6
  • 67
  • 89
9

The abc system doesn't include a way to declare an abstract instance variable. The code that determines whether a class is concrete or abstract has to run before any instances exist; it can inspect a class for methods and properties easily enough, but it has no way to tell whether instances would have any particular instance attribute.

The closest thing is probably a variable annotation:

class Bird(metaclass=ABCMeta):
    feathers : str
    ...

abc won't do anything with that annotation, but it at least expresses to readers and static type checkers that instances of Bird are supposed to have a feathers instance variable. (Whether static type checkers will understand that this instance variable is supposed to come from subclasses, I don't know.)

user2357112
  • 260,549
  • 28
  • 431
  • 505