Docorator Disected
Bengt Richter
bokr at oz.net
Sat Apr 2 16:28:36 EST 2005
On Sat, 02 Apr 2005 14:29:08 GMT, Ron_Adam <radam2 at tampabay.rr.com> wrote:
>
>I was having some difficulty figuring out just what was going on with
>decorators. So after a considerable amount of experimenting I was
>able to take one apart in a way. It required me to take a closer look
>at function def's and call's, which is something I tend to take for
>granted.
>
I think it might help you to start out with very plain decorators rather than
decorators as factory functions that return decorator functions that wrap the
decorated function in a wrapper function. E.g., (this could obviously be
parameterized as a single decorator factory, but I wanted to show the simplest level
of decorator functionality)
>>> def decoa(f):
... f.decstr = getattr(f, 'decstr', '') + 'a'
... return f
...
>>> def decob(f):
... f.decstr = getattr(f, 'decstr', '') + 'b'
... return f
...
>>> def decoc(f):
... f.decstr = getattr(f, 'decstr', '') + 'c'
... return f
...
>>> @decoa
... @decoc
... @decob
... @decob
... def foo(): pass
...
>>> foo.decstr
'bbca'
I.e.,
@decoa
@decoc
def foo(): pass
is almost[1] exactly equal to (note calling order c then a)
def foo(): pass
foo = decoc(foo)
foo = decoa(foo)
[1] One difference is that foo = deco(foo) is a RE-binding of foo,
and the binding wouldn't happen at all if the @deco version
raised an exception in deco. E.g.,
>>> def deco(f): raise NotImplementedError
...
foo not yet defined:
>>> foo
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: name 'foo' is not defined
Try the bad decorator:
>>> @deco
... def foo(): pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 1, in deco
NotImplementedError
No go, and foo still undefined:
>>> foo
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: name 'foo' is not defined
But the other way around:
bar undefined to start:
>>> bar
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: name 'bar' is not defined
Define it sucessfully:
>>> def bar():pass
...
>>> bar
<function bar at 0x02EE8B54>
Try to decorate the old-fashioned way:
>>> bar = deco(bar)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 1, in deco
NotImplementedError
>>> bar
<function bar at 0x02EE8B54>
Still there, defined as before (well, strictly speaking, not
necessarily as before: with bar already defined, deco could
have messed with the existing bar and THEN raised the exception).
Which would also happen with @deco, just that the new binding to bar
wouln't happen.
>>> def decobomb(f):
... f.bomb = 'bombed'
... raise Exception, 'Did it bomb the existing function?'
...
>>> def foo(): pass
...
>>> vars(foo).keys()
[]
>>> @decobomb
... def foo(): pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 3, in decobomb
Exception: Did it bomb the existing function?
>>> vars(foo).keys()
[]
Nope, seems that was a new foo that got bombed and then not
bound to replace the old foo.
>>> foo.bomb
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'function' object has no attribute 'bomb'
>I'm not sure this is 100%, or if there are other ways to view it, but
>it seems to make sense when viewed this way.
I like annotated code walk-throughs. But as others have pointed out,
it's still a bit buggy ;-)
Regards,
Bengt Richter
More information about the Python-list
mailing list