I still haven't got my head around decorators in Python.
I've already started using a lot of closures to do things like customize functions and classes in my coding.
Eg.
class Node :
def __init__(self,val,children) :
self.val = val
self.children = children
def makeRunner(f) :
def run(node) :
f(node)
for x in node.children :
run(x)
return run
tree=Node(1,[Node(2,[]),Node(3,[Node(4,[]),Node(5,[])])])
def pp(n) : print "%s," % n.val
printTree = makeRunner(pp)
printTree(tree)
As far as I can see, decorators are just a different syntax for doing something similar.
Instead of
def pp(n) : print "%s," % n.val
printTree = makeRunner(pp)
I would write :
@makeRunner
def printTree(n) : print "%s," % n.val
Is this all there is to decorators? Or is there a fundamental difference that I've missed?
A decorator is a function that takes in a function and returns an augmented copy of that function. When writing closures and decorators, you must keep the scope of each function in mind. In Python, functions define scope. Closures have access to the scope of the function that returns them; the decorator's scope.
Decorators are a very powerful and useful tool in Python since it allows programmers to modify the behaviour of a function or class. Decorators allow us to wrap another function in order to extend the behaviour of the wrapped function, without permanently modifying it.
You'll use a decorator when you need to change the behavior of a function without modifying the function itself. A few good examples are when you want to add logging, test performance, perform caching, verify permissions, and so on. You can also use one when you need to run the same code on multiple functions.
Python closures help avoiding the usage of global values and provide some form of data hiding. They are used in Python decorators.
While it is true that syntactically, decorators are just "sugar", that is not the best way to think about them.
Decorators allow you to weave functionality into your existing code without actually modifying it. And they allow you to do it in a way that is declarative.
This allows you to use decorators to do aspect-oriented programming (AOP). So you want to use a decorator when you have a cross-cutting concern that you want to encapsulate in one place.
The quintessential example would probably be logging, where you want to log the entry or exit of a function, or both. Using a decorator is equivalent to applying advice (log this!) to a joinpoint (during method entry or exit).
Method decoration is a concept like OOP or list comprehensions. As you point out, it is not always appropriate, and can be overused. But in the right place, it can be useful for making code more modular and decoupled.
Are your examples real code, or just examples?
If they're real code, I think you overuse decorators, probably because of your background (i.e. you are used to other programming languages)
def run(rootnode, func):
def _run(node): # recursive internal function
func(node)
for x in node.children:
_run(x) # recurse
_run(rootnode) # initial run
This run method obsoletes makeRunner. Your example turns to:
def pp(n): print "%s," % n.val
run(tree, pp)
However, this ignores completely generators, so…
class Node :
def __init__(self,val,children) :
self.val = val
self.children = children
def __iter__(self): # recursive
yield self
for child in self.children:
for item in child: # recurse
yield item
def run(rootnode, func):
for node in rootnode:
func(node)
Your example remains
def pp(n): print "%s," % n.val
run(tree, pp)
Note that the special method __iter__
allows us to use the for node in rootnode:
construct. If you don't like it, just rename the __iter__
method to e.g. walker
, and change the run
loop into: for node in rootnode.walker():
Obviously, the run
function could be a method of class Node
instead.
As you see, I suggest you use directly run(tree, func)
instead of binding them to the name printTree
, but you can use them in a decorator, or you can make use of the functools.partial
function:
printTree= functools.partial(run, func=pp)
and from then on, you would just
printTree(tree)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With