Reputation: 1410
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
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:
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
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
The core Exodus class loads migrations from a specified directory.
Each file will have a class that extends the BaseMigration object.
The BaseMigration's metaclass will register itself with a central list of migrations at parse time.
Upvotes: 1