On Sat, 02 Apr 2005 14:29:08 GMT, Ron_Adam <[EMAIL PROTECTED]> 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 -- http://mail.python.org/mailman/listinfo/python-list