A primer on Python decorators
thumbtack.com
thumbtack.com
http://stackoverflow.com/questions/739654/understanding-pyth...
The source code is pitifully small: https://bitbucket.org/mitsuhiko/flask/src/4d82231621fc/flask...
https://github.com/mitsuhiko/flask/blob/master/flask/app.py#...
When you call `route` with a URL pattern, it returns an inner function which is used as the decorator. That decorator just records your route and function in the Flask URL map, and returns your function unchanged.
So, Flask is arguably perpetuating a slight abuse of decorators, since it doesn't decorate or wrap your function at all, but merely saves a reference to it somewhere. But it's a fairly clean way to make up for the lack of code blocks or multi-statement anonymous functions in Python.
Since decorators (generally) run at module load time, any stateful decorator (usually) implies the use of global mutable state, which is (considered by many to be) the root cause of much bad design, convoluted flow, limited reusability and untestability. This is perhaps why most decorators in the standard library are pure (off the top of my head).
This is well beyond the scope of the post but an important and often overlooked point in my opinion.
Typically, doing this:
@register
def foo():
….
is bad, but this is much better: @registry.register
def foo():
…
if registry is a global object that is not a singleton. In that case, you can easily use distinct registries (for testing, etc…) and this is not much of an issue in practice. Another way of doing this kind of things is to post_pone the registration, with the decorator just labelling things: @register
def foo():
…
def register(f):
return WrappedFunc(f)
and then build an explicit list of modules being imported, and look for every instance of WrappedFunc in that module locals(). I use this in my own packaging project where people can define multiple python scripts that hook into different stages of the packaging.Thanks!
In this case it's better because you can avoid computing an extra hash of the object in cases where it's already a key of the dictionary. This may seem like a silly optimization, but it can very easily add up if you're accessing existing elements most of the time--I once had a bit of code that went 60x faster when I replaced an if-else with a try-except.
In other cases it can be even more beneficial. Say you're opening a file. One approach to avoid errors would be to check if a file exists first. This is error-prone because the file might cease to exist in between the 'if' and the 'open' statements, and now you have no code written to handle the error. Using try-except will ensure that you actually handle the error intelligently.
This isn't to say there's never a good reason to use an 'if' to check things, just that if you can do it in one step instead of two, one is usually better.
I am not sure if "try except" is really faster than "if else" in some edge cases in a memoization context, as you claim.
What I am sure is that in a didactic context, where you want people to understand code, something like:
if args not in stored_results:
stored_results[args] = fn(*args)
return stored_results[args]
is much better than that (from the OP): try:
# try to get the cached result
return stored_results[args]
except KeyError:
# nothing was cached for those args. let's fix that.
result = stored_results[args] = fn(*args)
return resultAs far as people understanding code - this is a common pattern in python code, try / except should be easy for anyone to understand.
>>> from timeit import timeit
>>> timeit(setup='x=dict([(i,i*2) for i in range(10)])',stmt=
"""
if 20 in x:
pass""")
0.07420943164572691
>>> timeit(setup='x=dict([(i,i*2) for i in range(10)])',
stmt="""
try:
x[20]
except KeyError:
pass""")
1.1514457843105674http://docs.python.org/library/stdtypes.html#dict.setdefault
Earlier, he uses the map function for no apparent reason. I mean - list comprehension would perfectly fit here.
I still think I have something to learn from the article, though.
> unlike in Java, you can also call a class method on an instance
It's possible in Java, too. It's just considered bad practice.
Still, a very nice read!