X33
X33

Reputation: 1410

Python OOP importing child class from parent class

Every business rule I create, it gets placed in a folder called "rule_definitions."

I have a class Rule. It acts as a template for all these created rules that they can inherit necessary generic functions from.

An example of the folder contents:

rule_definitions/
    discard.py
    saveValue.py
    storeValue.py
    writeToOutput.py

But if you want to create an instance of a rule, it's very clean to just do:

rule = Rule('discard') and have the Rule class do the importing itself, rather than finding the appropriate rule module every time you need to load a rule.

For example, the content of rule_definitions.py might be:

from Rule import Rule

class _discard(Rule):
    ...

And within Rule, we might have:

import importlib

class Rule():
    """A template class that all rules inherit from and are instantiated from."""

    def __init__(self, rule):
        # load the rule by name from rule definitions
        try:
            self.Rule = importlib.import_module('defs._'+ rule)
        except ImportError:
            print 'Failed to find rule definiton while attempting to load


    #other generic functions to inherit below

Is this the cleanest way? I'm not sure if this would be standard practice.

Upvotes: 0

Views: 264

Answers (2)

viraptor
viraptor

Reputation: 34155

There are a few issues here. Let me do it one by one:

  • What you propose doesn't really require _discard to inherit from Rule. You're using both inheritance and composition, which looks confusing.

  • The rule you return still has the type Rule, not _discard. Maybe that's what you want, maybe not...

  • Rule('discard') cannot be statically checked and won't get autocompletion you expect (if you're using IDE).

Here are some ideas for improvements:

  • Pure composition: Keep the current initialisation, but don't inherit from Rule. Either use another class for the generic functionality, or pass the parent into _discard constructor. (the second one will produce reference loops though)

With another class for generic functionality it can look like this:

class RuleTrait(object):
    def some_common_stuff(self):
        ...

class _discard(RuleTrait):
    def do_work(self):
        some_common_stuff()

class Rule(object):
    def __init__(self, typ):
        self.impl = _get_impl_by_name(typ)

    def __getattr__(self, name):
        return getattr(self.impl, name)

Rule("discard").do_work()  # this will be forwarded to _discard.do_work
  • Actual inheritance: Don't use the generic constructor, but the direct classes instead. Rule('discard') is not worse than rules.Discard(), and it will give you better type checks. And you can get rid of the import magic.

  • If you need more import magic in some cases, make it a factory instead. Rule(...) can only create a Rule object. But you can write a make_rule('discard') instead with whatever implementation you need.

Upvotes: 1

Rebs
Rebs

Reputation: 4239

What you're doing looks similar to dynamically loading database migrations. Django and similar database related projects may have code you could look at.

I wrote some code that does automatic loading and importing. Look at the load_migrations function I wrote here https://github.com/someones/exodus/blob/master/exodus/init.py

  1. The core Exodus class loads migrations from a specified directory.

  2. Each file will have a class that extends the BaseMigration object.

  3. The BaseMigration's metaclass will register itself with a central list of migrations at parse time.

Upvotes: 1

Related Questions