0

UPDATE: I am having trouble getting the names of inherited/derived class names declared in other modules in the base class module.

Use case: I would like to create a common interface to call the subclasses from the base class.

Example:

#basemodule.py
class BaseClass(object):
    @abstractmethod
    def method(self, inputs=None):
        pass

def get_subclasses():
    #I want derived class information here
    for cls in list(BaseClass.__subclasses__):
        # call subclasses here

# module1.py
import BaseClass
class derivedClass1(BaseClass):
    def __init__(self):
        super().__init__()
    def method(self, inputs):
        # method implemented in derivedClass1

# module2.py
import BaseClass
class derivedClass2(BaseClass):
    def __init__(self):
        super().__init__()
    def method(self, inputs):
        # method implemented in derivedClass2

Any suggestions are appreciated! Thanks :)

10
  • If you don't import the module, the parent class cannot know about subclasses. Just because you have some file somewhere on disc that uses the parent class doesn't mean the parent class knows about it. Commented Sep 26, 2022 at 7:06
  • @deceze, thanks for your reply. Importing modules of subclasses in parent class leads to circular import. Any suggestions on how to proceed? Commented Sep 26, 2022 at 7:12
  • What's the use case to begin with? Parents generally shouldn't be concerned about their children… Commented Sep 26, 2022 at 7:15
  • In the baseclass module, I want to call the method in all the subclasses and use their results in baseclass module, and number of subclasses might increase later. Commented Sep 26, 2022 at 7:20
  • That sounds like bad upside-down design. Again: what's the use case exactly? Again: consider that you may have a .py file anywhere on disk in which a class inherits the parent class. How may you reasonably discover all those files from a clueless parent class?! Commented Sep 26, 2022 at 7:22

1 Answer 1

2

Your question is a little bit vague, and lacks important details, like the error you're getting, and a bit more context regarding your actual goals here. Having said that, I've noticed a couple of things that might be the cause of the problems you're having:

  1. BaseClass.__subclasses__ is a method, therefore, you need to call it instead of accessing it, like you access a class property, or attribute. To do so, use BaseClass.__subclasses__() instead.
  2. In order to your get_subclasses() function to work, you need to first import the subclasses. Otherwise python won't know which classes inherit from BaseClass.

Corrected code

Here's the correct implementation of get_subclasses() function, as mentioned above:


from basemodule import BaseClass


def get_subclasses():
    """Get subclasses from `basemodule.BaseClass`."""
    for cls in list(BaseClass.__subclasses__()):
        # call subclasses here
        print(cls.__name__)  # Added print statement to test the solution.

Example

Without importing module1, and module2

Here's an example of the output from get_subclasses() I receive, when I don't import the modules that host the subclasses:

First Example

Importing module1, and module2

When I import both modules that host the subclasses, you then get the output I think you're expecting:

enter image description here

Full-code

Here's the full code of the examples:


# my_pckg/basemodule.py

from abc import ABCMeta, abstractmethod


class BaseClass(object):

    @abstractmethod
    def method(self, inputs=None):
        pass

# ================================================
# my_pckg/module1.py

from my_pckg.basemodule import BaseClass


class derivedClass1(BaseClass):

    def __init__(self):
        super().__init__()

    def method(self, inputs):
        # method implemented in derivedClass1
        pass


# ================================================
# my_pckg/module2.py

from my_pckg.basemodule import BaseClass


class derivedClass2(BaseClass):
    def __init__(self):
        super().__init__()

    def method(self, inputs):
        # method implemented in derivedClass2
        pass


# ================================================
# my_pckg/test.ipynb

from basemodule import BaseClass
from module1 import *
from module2 import *


def get_subclasses():
    """Get subclasses from `basemodule.BaseClass`."""
    for cls in list(BaseClass.__subclasses__()):
        # call subclasses here
        print(cls.__name__)  # Added print statement to test the solution.


get_subclasses()
# Prints:
# derivedClass1
# derivedClass2

Important notes

The imports as shown in the example pictures won't work, if you're trying to use them from outside the parent module. In the example I just gave, here's the complete tree view of the entire package structure:


my_pckg
|______init__.py    # <-- Needed to make my_pckg submodules "importable".
|____basemodule.py  # <-- Hosts the BaseClass class.
|____module1.py     # <-- Hosts the derivedClass1 subclass.
|____module2.py     # <-- Hosts the derivedClass2 subclass.
|____Test.ipynb     # <-- Where the test from the screenshots took place.

If you want to import these modules from outside the package you have two options:

  1. Create a setup for you package, and pip install it (use the -e flag to install it in development mode).
  2. Import sys, and add my_pckg path to the known paths.
import sys

sys.path.insert(0, './my_pckg')
from basemodule import BaseClass
from module1 import *
from module2 import *


def get_subclasses():
    """Get subclasses from `basemodule.BaseClass`."""
    for cls in list(BaseClass.__subclasses__()):
        # call subclasses here
        print(cls.__name__)  # Added print statement to test the solution.


get_subclasses()
# Prints:
# derivedClass1
# derivedClass2

For example:

enter image description here

Circular Imports

Do NOT import module1, and module2 inside basemodule, as this leads to a circular import. This happens because when you import basemodule, python will see that the module needs to import module1, and module2 and therefore goes to these modules. There, it finds out that both actually require basemodule themselves, so it goes back to basemodule. You can see that this becomes an infinite circle, where no module is able to be imported. To overcome this, place get_subclasses() function in a separate module, alongside all your necessary imports, like the example pictures.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.