On Jul 23, 11:25 am, Jason <[EMAIL PROTECTED]> wrote: > On Jul 23, 2:13 am, james_027 <[EMAIL PROTECTED]> wrote: > > > Hi, > > > I am learning python by learning django, and I stumble upon decorator > > which is very cool, any beginners resources for python decorators, > > although I can google it, I just want to get a good tutorial for this > > topic. > > > Thanks > > james
With apologies, there is an error in my previous WrapWithHelpDebug. The last part of the function should read: # Back in the WrapWithHelpDebug scope. # HelpDebug is now a function objected defined in this scope. return HelpDebug # This line was missing Now, my prior post shows how decorators work. Functions are objects created by Python's def statement. They have access to the names in all their enclosing scopes: the module's global scope and any functions that they are nested in. The WrapWithHelpDebug decorator wraps a function so uncaught exceptions get caught, logged, and the Python debugger is started. You won't notice much output from the logger usually, because the default log level is set to "DEBUG". That might be fine, but maybe you want to pass the debugging level as a parameter. import logging def WrapWithHelpDebug(func, logLevel): """Returns a function object that transparently wraps the parameter with the HelpDebug function""" # The def statement actually defines a function object and binds # it to a name. Since it is nested in this function, the def # statement isn't evaluated until def HelpDebug(*args, **keyargs): "Assist with debugging a function." # The func argument is a Pythong function object # arg is the positional arguments that follow the # first parameter, and keyargs has the keyword arguments. try: # The name "func" comes from the outer scope, WrapWithHelpDebug returnValue = func(*args, **keyargs) return returnValue except SystemExit: raise # Reraise the system exit exception except Exception, error: from logging import log from sys import exc_info import pdb log(logLevel, "Caught Exception: %s", error) # Start the debugger at the place where the # exception occurred pdb.post_mortem(exc_info()[2]) return # Nothing to return when an exception occurred # Back in the WrapWithHelpDebug scope. # HelpDebug is now a function objected defined in this scope. return HelpDebug def DivXY(x, y): "Divides X by Y" return x / y DivXY = WrapWithHelpDebug(DivXY, logging.DEBUG) # Debug the following calls DivXY(5.0, 2.1) # This will succeed DivXY(10.0, 0.0) # Causes a ZeroDivisionError exception So, if we just need to add a new parameter, how do we do that with a decorator? Could we use "@WrapWithHelpDebug(logging.DEBUG)"? No. Remember, the decorator symbol is just a bit of syntactic sugar. It turns: @WrapWithHelpDebug(logging.DEBUG) def DivXY(x, y): "Divides X by Y" return x / y Into: def DivXY(x, y): "Divides X by Y" return x / y DivXY = WrapWithHelpDebug(logging.DEBUG)(DivXY) Oops! That implies that we're calling "WrapWithHelpDebug" with our logging parameter. To accommodate the extra parameter, we're going to need to use Python's nested scopes again. Here's a final version that does everything we want: import logging def HelpDebugDecorator(logLevel): "Returns a function object that will properly decorate a function." # Note that logLevel used in the nested function HelpDebug # comes from this scope. def WrapWithHelpDebug(func): """Returns a function object that transparently wraps the parameter with the HelpDebug function""" # The def statement actually defines a function object and binds # it to a name. Since it is nested in this function, the def # statement isn't evaluated until def HelpDebug(*args, **keyargs): "Assist with debugging a function." # The func argument is a Pythong function object # arg is the positional arguments that follow the # first parameter, and keyargs has the keyword arguments. try: # The name "func" comes from the outer scope, WrapWithHelpDebug returnValue = func(*args, **keyargs) return returnValue except SystemExit: raise # Reraise the system exit exception except Exception, error: from logging import log from sys import exc_info import pdb log(logLevel, "Caught Exception: %s", error) # Start the debugger at the place where the # exception occurred pdb.post_mortem(exc_info()[2]) return # Nothing to return when an exception occurred # Back in the WrapWithHelpDebug scope. # HelpDebug is now a function objected defined in this scope. return HelpDebug # Back in the HelpDebugDecorate scope. # Return the WrapWithHelpDebug function object return WrapWithHelpDebug @HelpDebugDecorator(logging.ERROR) def DivXY(x, y): "Divides X by Y" return x / y #DivXY = WrapWithHelpDebug(DivXY, logging.DEBUG) # Debug the following calls DivXY(5.0, 2.1) # This will succeed DivXY(10.0, 0.0) # Causes a ZeroDivisionError exception Just a few final notes about decorators: o Multiple decorators can be applied to a function. o Any callable (such as classes and instances with a __call__ method) can be used as a decorator. This can be tremendously useful if you need to save state between multiple decorated calls. For example, you might want the log when certain functions are called. Perhaps you want to indent each successive call, and dedent with every decorated return. Use an instance to keep track of the indents and dedents. I hope that I haven't made any egregious errors in these two posts. However, decorates are a very nifty Python feature, and I hope this helps you understand them. --Jason -- http://mail.python.org/mailman/listinfo/python-list