use
def Factory(class_type):
factory = {
'class1':class1,
'class2':class2,
'class3':class3}
try:
return factory[class_type]()
except KeyError:
pass
metaclasses are your friend, here is one I use frequently for factoires.
class Register(type):
"""A tiny metaclass to help classes register themselves automagically"""
def __init__(cls, name, bases, dict):
if ('register' in dict): # initial dummy class
setattr(cls, 'register', staticmethod(dict['register']))
elif (getattr(cls, 'DO_NOT_REGISTER', 0)):
# we don't want to register this non-concrete class
delattr(cls, 'DO_NOT_REGISTER')
elif (object not in bases):
cls.register(name, cls)
return
class Widget(object):
__metaclass__ = Register
all = {}
def register(name, cls):
Widget.all[name] = cls
def factory(name):
return Widget.all[name]
factory = staticmethod(factory) # just use this class like a namespace
class C1(Widget): pass
class C2(Widget): pass
class C3(Widget): pass
# instantiate a C3 by name
c3_instance = Widget.factory('C3')()
Every class that inherits Widget and doesn't have a DO_NOT_REGISTER attribute
will put it's name:class into the Widget.all dictionary.
I typically use a short hierarchy to break up the code a bit
class WidgetFacory(object):
"""this class only has static factory methods"""
__metaclass__ = Register
all = {}
# register() and factory() as above
class Widget(WidgetFactory): # we inherit 'WidgetFactory' to get the metaclass
"""This class is the base functionality of Widget classes,
This is a virtual base class for Widgets that can't be instatiated
by the WidgetFactory interface"""
DO_NOT_REGISTER = 1 # tell the factory to forget we exist
def __init__(self, *args, **opts):
# your code here
# now define the classes that can be instantiated from the WidgetFactory
class Mallet(Widget): pass
class Hoop(Widget): pass
class Ball(Widget): pass
hmm, the post is about to get longer.
If you repeat the above pattern enough, write a func to do all the above
for you, so your code might look like
class Widget(object): pass # your real definition of the base Widget class
# setup the factory, and rebind the name Widget to a class that inherits it
(WidgetFactory, Widget) = make_factory(Widget)
I use a slightly different technique in my unit tests, cut-n-pasted here
it defines the class-tracker using Register and returns the base object.
It also defines a main() method for the test suite. My test-coverage
class is in here too, but that is extra. It just defines a TestCase
that checks for test coverage versus implemented tests.
Last things first, here is how it is used
""" t_libUtil.py - test 'libUtil' module """
import test # defined below
import libUtil
(Test, main) = test.setup(libUtil)
class SimpleSum(Test):
we_test = [libUtil.simple_sum]
def test_sum(self):
f = libUtil.simple_sum
x = [0,0]
self.assertEqual([0,3], f(x,[1,3]) or x)
if (__name__ == '__main__'):
main()
# module 'test.py'
import unittest
class Register(type):
"""A tiny metaclass that keeps track of all classess"""
def __init__(cls, name, bases, dict):
if ('register' in dict): # initial dummy class
setattr(cls, 'register', staticmethod(dict['register']))
elif (object not in bases):
cls.register(name, cls)
return
def setup(module):
class Test(unittest.TestCase, object): # TestCase is an old-style class
__metaclass__ = Register
alltests = [] # all Test classes
allfuncs = [] # list of all the functions they test
def register(name, cls):
Test.alltests.append(cls)
Test.allfuncs += cls.we_test
class TestCoverage(Test):
"""make sure we have tests for all the funcs and classes in the module"""
we_test = []
def test_coverage(self):
# find all functions and classes in the module
need = []
for (name, val) in module.__dict__.items():
if (callable(val)):
need.append(val)
try:
if (val.__class__.__name__ == name): # this finds classes defined in the module
need.append(val)
except: pass
for (ob) in need:
good = ob in Test.allfuncs
if (not good):
raise self.failureException("No Test coverage: %s" % (str(ob)))
def main():
suite = unittest.TestSuite()
test_classes = Test.alltests
for cls in test_classes:
suite.addTest(unittest.makeSuite(cls))
result = unittest.TestResult()
suite(result)
msg = []
if (result.errors):
msg.append('Failure')
if (result.errors):
msg.append('Error')
print "%s: %d %s" % (module.__name__, result.testsRun, " & ".join(msg) or 'Success')
for (err) in result.errors:
print "\n".join(map(str, err))
for (failure) in result.failures:
print "\n".join(map(str, failure))
return
return (Test, main)
In places like unittest where we already do heavy introspection
using metaclasses to do the introspecition is usually a big win.
long-posted-ly,
-jackdied