On Tue, Oct 3, 2017 at 4:41 AM, Steve D'Aprano <steve+pyt...@pearwood.info> wrote: > On Tue, 3 Oct 2017 06:51 am, Bill wrote: > >> Can you inspire me with a good decorator problem (standard homework >> exercise-level will be fine)? > > > Here is a nice even dozen problems for you. Please ask for clarification if > any > are unclear. > > > > (1) Write a decorator which simply prints a descriptive message and the name > of > the decorated function once, when the function is first decorated. > > E.g. if you write: > > @decorate > def spam(x): > return x + 1 # for example > > print(spam(1)) > print(spam(2)) > > > Python should print: > > Decorating function spam. > 2 > 3 > > > Note: "spam" must not be hard-coded, it must be taken from the function being > decorated. (Hint: all functions have their name available as func.__name__.) > > > (2) Modify the decorator from (1) so that calling the wrapped function also > print a descriptive message such as "Calling function spam". The expected > output will be: > > Decorating function spam. > Calling function spam. > 2 > Calling function spam. > 3 > > > (3) Write a decorator that checks that the decorated function's first argument > is a non-empty string, raising an appropriate exception if it is not, and lets > through any other arguments unchanged. > > > (4) Same as above, except the first argument is automatically stripped of > leading and trailing whitespace and forced to uppercase. > > > (5) Write a decorator which injects the argument 10 into the list of arguments > received by the wrapped function. E.g. if you write: > > @inject > def add(a, b): > return a + b > > @inject > def sub(a, b): > return a - b > > print(add(5), sub(5)) > > Python should print "15 5". (And *not* "15 -5".) > > > (6) [ADVANCED] Modify the decorator in (5) so that it takes an argument > telling > it what value to inject into the list of arguments: > > @inject(99) > def sub(a, b): > return a - b > > print(sub(5)) > > will now print "94". > > > (7) Write a decorator which checks the decorated function's two arguments are > given smallest first, swapping them around if needed. > > > (8) Write a decorator which prints the name of the wrapped function, its > arguments, and the time, each time the wrapped function is called. > > > (9) [ADVANCED] Modify the decorator from (8) to take an argument specifying > the > path to a file, and use the logging module to log the details to that file > instead of printing them. > > > (10) Write a decorator which adds an "cache" attribute initialised to an empty > dictionary to the decorated function. > > > (11) Write a decorator which wraps a class (not function!), and adds a "help" > method to the class which prints a message as shown below. For example: > > @addhelp > class Spam: > pass > > @addhelp > class Eggs: > pass > > x = Spam() > x.help() > y = Eggs() > y.help() > > will print: > > See http://example.com/Spam > See http://example.com/Eggs > > (Hint: classes also have a __name__ attribute.) > > > (12) [ADVANCED] Write a decorator which wraps a class, and applies the > decorator > from (10) above to each non-dunder¹ method in the class. That is, after: > > @addcaches > class MyClass: > def foo(self): > pass > def bar(self): > pass > > print(MyClass.foo.cache, MyClass.bar.cache) > > should print "{} {}". > > > > ¹ Remember that dunder methods are those that start with two leading and > trailing underscores: "Double UNDERscore" methods.
I also suggest: (13) Modify the decorator from (8) so that the wrapper has the same name and doc string as the wrapped function. (Hint: use functools.wraps) -- https://mail.python.org/mailman/listinfo/python-list