5 ms·
Charming Python: Decorators make magic easy
- blasdel 17y agoI'm inordinately fond of using decorators for adding metadata attributes as markup on functions: def add_example(*args): def decorate(fn): if not hasattr(fn, 'examples'): fn.examples = [] fn.examples.insert(0, args) return fn return decorate I use decorators like this to add extra command-line options to methods, annotate what types to construct from a command-line string, add typed __doc__ extensions, note example input/output for unit testing and documentation, etc.
- ilyak 17y agoNice! This makes python quite less boring.
- silentbicycle 17y agoI'm genuinely curious, not trying to start flaming, but does this increase the expressiveness of the underlying language? Or is it mainly useful as a syntactic shortcut for function composition (as the article implies)? @dec1 @dec2 @dec3 def some_func(arglist): ... vs. some_func = dec1(dec2(dec3(fun some_func(arglist) ...))) I feel like I'm missing something, but perhaps I'm just used to using other idioms for this.
- stevejohnson 17y agoYes, it is syntactic sugar for composition. It exists to make source code more self-documenting because you can see that a function is "decorated" at the point of its definition, rather than some other location where the conceptual context is lost. The best common use case I can think of for it is web interfaces with access control: @login_required def profile(...): #... @admin_required def ban_user(...): #... Decorators have also been used for great justice in unit testing frameworks and GUI libraries. Your example does look a little overzealous, which is why people don't normally use decorators in that way. Usually you'll only see one decorator per function.
- silentbicycle 17y ago> at the point of its definition Ok, that makes sense, thanks. I guess needing syntax for this is a consequence of Python's quirky lambdas. In languages with full lambdas, you can just wrap an anonymous function at the point of definition. profile = login_required(function (...) ...) -- in Lua (define profile (login-required (lambda (...) ...))) ; in Scheme let profile = login_required(fun ... -> ...) (* in OCaml *) Putting the decorator on its own line draws attention to it, though. This is probably also another instance of Python's "flat is better than nested" design choice.