Skip to content

Instantly share code, notes, and snippets.

@francisco-perez-sorrosal
Last active April 24, 2019 18:41
Show Gist options
  • Save francisco-perez-sorrosal/5ade6de19578c97be85939595c55ea40 to your computer and use it in GitHub Desktop.
Save francisco-perez-sorrosal/5ade6de19578c97be85939595c55ea40 to your computer and use it in GitHub Desktop.
Object Builder for Class Hierarchies
class BaseClass(object): pass
class Hello(BaseClass):
"""Example naive class"""
def __init__(self, arg1):
self.arg1 = arg1
def __repr__(self):
return "arg1 {}".format(self.arg1)
class HelloWorld(BaseClass):
"""Example naive class"""
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
def __repr__(self):
return "arg1 {}\narg2 {}".format(self.arg1, self.arg2)
class BaseBuilder:
"""Base Class to be inherited by the specific builders. For example,
take a look at: HelloBuilder and HelloWorldBuilder below"""
@abstractmethod
def __call__(self, *args, **kwargs):
pass
class HelloBuilder(BaseBuilder):
def __init__(self):
self._instance: Optional[Hello] = None
def __call__(self, arg1, **_ignored) -> Hello:
if not self._instance:
self._instance = Hello(arg1, arg2)
return self._instance
class HelloWorldBuilder(BaseBuilder):
def __init__(self):
self._instance: Optional[HelloWorld] = None
def __call__(self, arg1, arg2, **_ignored) -> HelloWorld:
if not self._instance:
self._instance = HelloWorld(arg1, arg2)
return self._instance
class BaseClassObjectProvider(object):
def __init__(self) -> None:
self._builders: Dict[str, str] = {}
def register_builder(self, builder_id: str, builder_str: str) -> None:
print("Registering {}/{}".format(builder_id, builder_str))
self._builders[builder_id] = builder_str
def get(self, builder_id: str, **kwargs):
return self._create(builder_id, **kwargs)
def _create(self, builder_id: str, **kwargs) -> BaseClass:
builder_str = self._builders.get(builder_id)
if not builder_str:
raise ValueError(builder_id)
module_name, _, class_name = builder_str.rpartition('.')
module = importlib.import_module(module_name)
class_ = getattr(module, class_name)
if not issubclass(class_, BaseBuilder):
raise (AssertionError("Builder class {} should inherit from {}".format(class_, BaseBuilder.__qualname__)))
# Check all arguments in target class __call__ method are in kwargs
signature = inspect.signature(class_.__call__)
for param in list(signature.parameters)[1:-1]: # Skip first (self) and last (_ignored) arguments to check
if param not in kwargs.keys():
raise KeyError("Passed arguments to builder {} instantiation does not contain argument {}".format(class_, param))
instance = class_()
return instance.__call__(**kwargs)
# Usage
example_provider = BaseClassObjectProvider()
example_provider.register_builder("hello_builder", "HelloBuilder")
example_provider.register_builder("hello_world_builder", "HelloWorldBuilder")
...
my_object = example_provider.get("hello_builder", {"arg1": "hola", "arg2": "mundo"})
print(my_object) # Should print "hola"
my_object = example_provider.get("hello_world_builder", {"arg1": "hola", "arg2": "mundo"})
print(my_object) # Should print "hola mundo"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment