Search code examples
pythonclasspropertiesgetattr

How to access the subclasses of a class as properties?


So I have a .py file containing a class where its subclasses can be accessed as properties. All these subclasses are defined beforehand. I also need all the subclasses to have the same ability (having their own subclasses be accessible as properties). The biggest problem I've been facing is that I don't know how to access the current class within my implementation of __getattr__(), so that'd be a good place to start.

Here's some Python+Pseudocode with what I've tried so far. I'm pretty sure it won't work since __getattr__() seems to be only working with instances of a class. If that is case, sorry, I am not as familiar with OOP in Python as I would like.

class A(object):
    def __getattr__(self, name):
        subclasses = [c.__name__ for c in current_class.__subclasses__()]
        if name in subclasses:
            return name
    raise AttributeError

Solution

  • If I've understood your question properly, you can do what you want by using a custom metaclass that adds a classmethod to its instances. Here's an example:

    class SubclassAttributes(type):
        def __getattr__(cls, name):  # classmethod of instances
            for subclass in cls.__subclasses__():
                if subclass.__name__ == name:
                    return subclass
            else:
                raise TypeError('Class {!r} has no subclass '
                                'named {!r}'.format(cls.__name__, name))
    
    class Base(object):
        __metaclass__ = SubclassAttributes  # Python 2 metaclass syntax
    
    #class Base(object, metaclass=SubclassAttributes):  # Python 3 metaclass syntax
    #    """ nothing to see here """
    
    class Derived1(Base): pass
    class Derived2(Base): pass
    
    print(Base.Derived1)  # -> <class '__main__.Derived1'>
    print(Base.Derived2)  # -> <class '__main__.Derived2'>
    print(Base.Derived3)  # -> TypeError: Class 'Base' has no subclass named 'Derived3'
    

    For something that works in both Python 2 and 3, define the class as shown below. Derives Base from a class that has SubclassAttributes as its metaclass. The is similar to what the six module's with_metaclass() function does:

    class Base(type.__new__(type('TemporaryMeta', (SubclassAttributes,), {}),
                            'TemporaryClass', (), {})): pass