![](https://secure.gravatar.com/avatar/e4d005fd33f12f0ac7ef49852a35e7c2.jpg?s=120&d=mm&r=g)
Here's a little module about the power of decorators. We show decorators used in two capacities: 1. to invoke the built-in classmethod wrapper, giving us a way to invoke methods on a class with no "self objects" 2. to wrap a user-defined cost with a user-defined sales_tax with a flexible percentage (defaults to 10%). ==== taxes.py ==== from random import randint def sales_tax(sale, percent = 0.1): def vat(x): before = sale(x) after = (1 + percent) * before print("Before: %s; After: %s" % (before, after)) return after return vat class Register: shipping = 1.70 # fixed cost @sales_tax def __cost(price): return price + Register.shipping @classmethod def transact(klass, price): return klass.__cost(price) def test(): print(Register) for i in range(10): Register.transact(randint(1,100)) However, I'd consider the above too complicated, so refactor below: ==== taxes.py ==== from random import randint def sales_tax(sale, percent = 0.1): def vat(x): before = sale(x) after = (1 + percent) * before print("Before: %s; After: %s" % (before, after)) return after return vat # fixed cost shipping = 1.70 @sales_tax def cost(price): return price + shipping class Register: @classmethod def transact(klass, price): return cost(price) def test(): print(Register) for i in range(10): Register.transact(randint(1,100)) What's cooler is we're also demonstrating how decorator syntax makes as much sense outside a class definition, plus we're playing a different scope game, with Register reaching outside its class for a more global cost function (it really didn't have been inside the class def in the first example), which in turn relies on the global shipping variable. In the lesson plan I'm dreaming up, it'd be useful to show both examples, as we're showing off the freedoms you have when it comes to mixing functions and classes top level, in addition to decorator syntax. Regarding decorators, I think I good way to teach them *is* to recapitulate their motivation. Having to define the function first, before wrapping it in clunky f = g(f) syntax at the end, was making for less readable code. @ is Python's function composer, with the second argument a function def and source of the name, the first argument functional wrapper for the second i.e. @f def g(args) ... is the same as g = f(g(args)) Earlier in the buildup to such as the above, we'll look at simpler more algebraic examples of function composition .
def f(h): def k(x): return h(x) + 2 return k
@ f def g(x): return x**2
print(g(10)) 102 print(g(12)) 146
Then go for fancier user-defined examples, including with argument passing.
def f(a): if a == 1: def n(targ): def z(x): return targ(x) + 2 return z return n if a == 2: def n(targ): def z(x): return targ(x) + 3 return z return n
f(1)(g) <function z at 0x83c492c> f(1) <function n at 0x83c48ec> @f(1) def g(x): return x * x
g(10) 102 @f(2) def g(x): return x * x
g(10) 103
Kirby
participants (1)
-
kirby urner