newb question about @property

Steve D'Aprano steve+python at pearwood.info
Tue Oct 3 06:41:55 EDT 2017


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.


* * *


Bruce Eckel has an excellent introduction to Python decorators, from way back
when they were first introduced in 2008. His introduction is notable because:

- he points out explicitly that Python decorators are not the same as 
  the Decorator design pattern (I thought they were!);

- he recommends using a class as the decorator, and building the extra
  functionality in object oriented fashion, rather than functional 
  programming fashion (this may give an easier introduction to those
  who aren't familiar with functional idioms);

- and he correctly predicted that the introduction of the @ syntactic
  sugar would have a big impact on the way people think about Python 
  code.


http://www.artima.com/weblogs/viewpost.jsp?thread=240808

Feel free to read his post before trying the problems I set.



-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.




More information about the Python-list mailing list