Last active
December 16, 2015 13:29
-
-
Save honzajavorek/5442491 to your computer and use it in GitHub Desktop.
"Decorate to register"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### | |
### I want to collect some functions (or classes) for further use. | |
### | |
# Option 1: IMPLICIT (similar to CONVENTION OVER CONFIGURATION) | |
# Convention = all functions have the same prefix. | |
def compute_bullshit(...): | |
pass | |
def compute_butt(...): | |
pass | |
def compute_shiny_metal_ass(...): | |
pass | |
# Here we define a factory containing extremely unreadable, | |
# potentially dangerous (read as 'leading to mistakes'), | |
# and non-elegant magic to somehow figure out what to return. | |
def compute(name, *args, **kwargs): | |
fn = globals()['compute_' + name] | |
return fn(*args, **kwargs) | |
# Imagine at least 20 different 'whatifs', including: | |
# - What if the name is invalid and the function does not exist? | |
# - What if there occures a function prefixed 'compute_' which is | |
# for whatever reason not supposed to be called this way? | |
# - What if those functions are not in single module? more magic? | |
# - What if ... | |
# Option 2a: EXPLICIT (similar to CONFIGURATION OVER CONVENTION) | |
repository = {} | |
def compute_bullshit(...): | |
pass | |
def compute_butt(...): | |
pass | |
def compute_shiny_metal_ass(...): | |
pass | |
# This is brilliant! All explicit, clear, no magic, no mistakes... | |
# Wait... Is it brilliant? No. This is good for small cases, but later | |
# it is a lot of tedious work. Does not comply with DRY and laziness. | |
# No one wants to write this unless he/she is paid by hourly rate ;-) | |
repository['bullshit'] = compute_bullshit | |
repository['butt'] = compute_butt | |
repository['shiny_metal_ass'] = compute_shiny_metal_ass | |
def compute(name, *args, **kwargs): | |
fn = repository[name] | |
return fn(*args, **kwargs) | |
# Option 2b: EXPLICIT & DECLARATIVE | |
repository = {} | |
# decorator to explicitly register functions | |
def computation(name=None): | |
def wrapper(fn): | |
name = name or fn.__name__ | |
repository[name] = fn | |
return fn | |
return wrapper | |
# All explicit, clear, no magic, no mistakes, DRY | |
# approach. See it in action: | |
@computation('bullshit') # results in registry['bullshit'] = fn | |
def compute_bullshit(...): | |
pass | |
@computation() # results in registry['compute_butt'] = fn | |
def compute_butt(...): | |
pass | |
@computation('shiny_ass') # results in registry['shiny_ass'] = fn | |
def compute_shiny_metal_ass(...): | |
pass | |
def compute(name, *args, **kwargs): | |
fn = repository[name] | |
return fn(*args, **kwargs) | |
# Note: This would be even better... | |
def compute(name, *args, **kwargs): | |
try: | |
fn = repository[name] | |
except KeyError: | |
raise ValueError('No such computation available: {0}'.format(name)) | |
return fn(*args, **kwargs) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you for this brilliant example ;) (now I see what you meant)