9
\$\begingroup\$

The goal of the code below is to have an abstract base class that defines simple methods and attributes for the subclasses. This is part of an application that provides the code base for others to develop their own subclasses such that all methods and attributes are well implemented in a way for the main application to use them. The application shall never create more than one object of each subclass, since they are supposed to implement everything correctly to be called upon. Ideally, instantiating objects of the subclasses would not even be necessary, but is needed as it is in order to check if the abstract methods are implemented.

However, Python seems to be different and awkward when it comes to classes in comparison with other programming languages (initialization, attributes, properties), and I am not very sure if the solution below is the most appropriate. It seems too much complicated to achieve the aforementioned purpose. In addition, another function (not in the code below) must be made to properly check (make sure that they are of a certain type) the attributes of the subclasses.

Any thoughts?

from abc import ABC, abstractmethod

class abstract_attribute(object):
    """taken from http://stackoverflow.com/questions/32536176/how-to-define-lazy-variable-in-python-which-will-raise-notimplementederror-for-a/32536493#32536493"""
    def __get__(self, obj, type):   
        # Now we will iterate over the names on the class,
        # and all its superclasses, and try to find the attribute
        # name for this descriptor
        # traverse the parents in the method resolution order
        for cls in type.__mro__:
            # for each cls thus, see what attributes they set
            for name, value in cls.__dict__.items():
                # we found ourselves here
                if value is self:
                    # if the property gets accessed as Child.variable,
                    # obj will be done. For this case
                    # If accessed as a_child.variable, the class Child is 
                    # in the type, and a_child in the obj.
                    this_obj = obj if obj else type

                    raise NotImplementedError(
                         "%r does not have the attribute %r "
                         "(abstract from class %r)" %
                             (this_obj, name, cls.__name__))

        # we did not find a match, should be rare, but prepare for it
        raise NotImplementedError(
            "%s does not set the abstract attribute <unknown>", type.__name__)

class Base(ABC):
    # attr1 and attr2 must be properly (be of a certain type) implemented by subclasses
    # abstract_attribute() checks if the attribute is present in the subclass
    attr1 = abstract_attribute()
    attr2 = abstract_attribute()

    @abstractmethod
    def method():
        pass

    # a class property that does not change from instance to instance
    @property
    def prop(self):
        return self.prop
    @prop.setter
    @classmethod
    def prop(self, prop):
        self.prop = prop

class Sub(Base):
    # attr1 and attr2 must be implemented otherwise and exception is thrown upon accessing them
    attr1 = something
    attr2 = somethingElse
    # method must be implemented otherwise an exception is thrown upon instantiation
    def method():
        print("must be implemented by subclasses")
\$\endgroup\$
0

1 Answer 1

6
\$\begingroup\$

I don't see much of problems with using properties as abstract attributes e.g:

class Foo(abc.ABC):
    @property
    @abc.abstractmethod
    def bar(self):
        ...

However, if you still don't like it instead of going for your ninja stackoverflow way there is an easy way in python 3.6.

class Foo(abc.ABC):
    def __init_subclass__(cls, **kwargs):
        if not hasattr(cls, 'bar'):
            raise NotImplementedError('damn')
        return super().__init_subclass__(**kwargs)

As for one instance per class just look for python Singleton implementations.

\$\endgroup\$
4
  • \$\begingroup\$ This (stackoverflow.com/a/33201/2381631) one seems to work for the singleton part. However, your __init_subclass__ throws an AttributeError: 'super' object has no attribute '_Base__init_subclass using Python 3.6. \$\endgroup\$
    – dfernan
    Commented Jan 24, 2017 at 17:16
  • \$\begingroup\$ By the way, the reason for not having properties as abstract attributes is to avoid having the need to implement them all (and type a lot) in subclasses. Simply attr1 = something is way easier and straightforward right after the declaration of the class class Sub(Base): .... \$\endgroup\$
    – dfernan
    Commented Jan 24, 2017 at 17:20
  • \$\begingroup\$ @dfernan I forgot trailing __ fixed AttributeError. as for property with abstractmethod since those 2 are functions you can create your custom abstract_property function using those 2. \$\endgroup\$
    – Alex
    Commented Jan 24, 2017 at 17:34
  • \$\begingroup\$ Seems to work well now, thanks. This indeed looks like the cleaner option. \$\endgroup\$
    – dfernan
    Commented Jan 24, 2017 at 18:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.