- page 27
Pretty Decorators
Python decorators are cool, but they can become very messy if you sart taking arguments:
def decorate_with_args(arg):
def the_decorator(func):
def run_it():
func(arg)
return run_it
return the_decorator
@decorate_with_args('some string')
def messy(s):
print s
ugh. Three levels of function definitions for a single decorator. And heaven forbid you want the decorator to be useable without supplying any arguments (not even empty brackets).
So then, I present a much cleaner decorator helper class:
class ParamDecorator(object):
def __init__(self, decorator_function):
self.func = decorator_function
def __call__(self, *args, **kwargs):
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
# we're being called without paramaters
# (just the decorated function)
return self.func(args[0])
return self.decorate_with(args, kwargs)
def decorate_with(self, args, kwargs):
def decorator(callable_):
return self.func(callable_, *args, **kwargs)
return decorator
All that’s required of you is to take the decorated function as your first argument, and then any additional (optional) arguments. For example, here’s how you might implement a “pending” and “trace” decorator:
@ParamDecorator
def pending(decorated, reason='no reason given'):
def run(*args, **kwargs):
print "function '%s' is pending (%s)" % (decorated.__name__, reason)
return run
@ParamDecorator
def trace(decorated, label=None):
if label is None:
label = decorated.__name__
else:
label = "%s (%s)" % (decorated.__name__, label)
def run(*args, **kwargs):
print "%s: started (args=%s, kwargs=%s)" % (label, args, kwargs)
ret = decorated(*args, **kwargs)
print "%s: returning: %s" % (label, ret)
return ret
return run
Which can then be used as either standard or paramaterised decorators:
@pending
def a():
pass
@pending("I haven't done it yet!")
def b():
pass
@trace
def foo():
return "blah"
@trace("important function")
def bar():
return "blech!"
And just to show what this all amounts to:
if __name__ == '__main__':
a()
b()
foo()
bar()
reveals the following output:
function 'a' is pending (no reason given)
function 'b' is pending (I haven't done it yet!)
foo: started (args=(), kwargs={})
foo: returning: blah
bar (important function): started (args=(), kwargs={})
bar (important function): returning: blech!
Fairly simple stuff, but hopefully useful for anyone who finds themselves tripped up by decorators - particularly when trying to allow for both naked and paramaterised decorators.
P.S: I’ve made a pastie of the code in this post, because my weblog engine is not cool enough to colour-code python ;)
Too magic?
A snippet from the source code of my iPhone app, GRiS:
- (void) tableView: (UITableView*) tableView
willBeginEditingRowAtIndexPath: (NSIndexPath *) indexPath {
// the mere presence of this method causes a swipe action to be
// recognised, and a delete button appears. like magic!
}
I’m glad it was so easy, but fairly unnerved at the same time…
A bunch-o-great-videos
I feel a bit lame reblogging these when they’re all from the last 4 weeks of the b3ta newsletter, but many people don’t read it (you should!).
Remap shift+space to underscore
So I had this great idea yesterday (for coders, at least): remap [shift+space] to [underscore]. Turns out I am far from the first to think of this, which only enforces its awesomeness as an idea.
Mac:
Put this in ~/Library/KeyBindings/DefaultKeyBinding.dict
:
{
/* turn shift + space into underscore */
"$ " = ("insertText:", "_");
}
Linux: for a PC keyboard, try:
xmodmap -e 'keycode 65 = space underscore'
or on a mac keyboard:
xmodmap -e 'keycode 57 = space underscore'
Or if neither of those work, run:
xmodmap -pk | grep space | awk '{print $1}'
and use that number instead of 65 or 57 above.
You can put this keybinding in ~/.xmodmaprc or somesuch if you like it.
Paradigms lost: The Windows 7 Taskbar versus the OS X Dock
the first two pages are mandatory reading for anyone who ever complained that “mac apps don’t close properly”