Python decorators

1,569 views

Published on

Published in: Technology
0 Comments
7 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,569
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
66
Comments
0
Likes
7
Embeds 0
No embeds

No notes for slide
  • Like onion
  • Like onion
  • For example, @Transactional annotation in java
  • AOPUse decorator to validate argumentsLoggingCaching
  • Python 3.0
  • The root of the problem here is in the self argument of the tracer class’s __call__ method—is it a tracer instance or a Person instance? We really need both as it’s coded: the tracer for decorator state, and the Person for routing on to the original method. Really, self must be the tracer object, to provide access to tracer’s state information; this is true whether decorating a simple function or a method. Unfortunately, when our decorated method name is rebound to a class instance object with a __call__, Python passes only the tracer instance to self; it doesn’t pass along the Person subject in the arguments list at all. Moreover, because the tracer knows nothing about the Person instance we are trying to process with method calls, there’s no way to create a bound method with an instance, and thus no way to correctly dis- patch the call.
  • If you want your function decorators to work on both simple functions and class methods, the most straightforward solution lies in using one of the other state retention solutions described earlier—code your function decorator as nested defs, so that you don’t depend on a single self instance argument to be both the wrapper class instance and the subject class instance. It can store both state by nonlocal attribute and pass arguments
  • Explicit syntax Decorators make augmentation explicit and obvious. Their @ syntax is easier to recognize than special code in calls that may appear anywhere in a source file—in our singleton and tracer examples, for instance, the decorator lines seem more likely to be noticed than extra code at calls would be. Moreover, decorators allow function and instance creation calls to use normal syntax familiar to all Python programmers. Code maintenance Decorators avoid repeated augmentation code at each function or class call. Be- cause they appear just once, at the definition of the class or function itself, they obviate redundancy and simplify future code maintenance. For our singleton and tracer cases, we need to use special code at each call to use a manager function approach—extra work is required both initially and for any modifications that must be made in the future. Consistency Decorators make it less likely that a programmer will forget to use required wrap- ping logic. This derives mostly from the two prior advantages—because decoration is explicit and appears only once, at the decorated objects themselves, decorators promote more consistent and uniform API usage than special code that must be included at each call. In the singleton example, for instance, it would be easy to forget to route all class creation calls through special code, which would subvert the singleton management altogether.
  • Python decorators

    1. 1. Python DecoratorsAlex Su Classification 4/3/2012 Copyright 2009 Trend Micro Inc. 1
    2. 2. Before• Java Jersey@GET@PATH(―/hello‖)public void hello() { return ―Hello World‖}• Python Bottle@get(/hello)def hello(): return "Hello World!‖ Copyright 2009 Trend Micro Inc.
    3. 3. What’s a Decorator?Decoration is a way to specify management code for functions and classes.Decorators themselves take the form of callable objects (e.g., functions) thatprocess other callable objects.• Function decorators install wrapper objects to intercept later function calls and process them as needed.• Class decorators install wrapper objects to intercept later instance creation calls and process them as required. Copyright 2009 Trend Micro Inc.
    4. 4. Why Decorators?• Decorators have a very explicit syntax, which makes them easier to spot than helper function calls that may be arbitrarily far-removed from the subject functions or classes.• Decorators are applied once, when the subject function or class is defined; it’s not necessary to add extra code (which may have to be changed in the future) at every call to the class or function.• Because of both of the prior points, decorators make it less likely that a user of an API will forget to augment a function or class according to API requirements. Copyright 2009 Trend Micro Inc.
    5. 5. Why Decorators?def query(): @get(/query) # parse http request @logging # logging @authentication # authenticate user @authorization(―admin‖) # authorize user permission @cache # if cache exists, return it. def query(): # get data from DB # get data from DB Copyright 2009 Trend Micro Inc.
    6. 6. Function Decorators Classification 4/3/2012 Copyright 2009 Trend Micro Inc. 6
    7. 7. UsageIn terms of code, function decorators automatically map the following syntax:@decoratordef F(arg):...F(99) # Call functioninto this equivalent form, where decorator is a one-argument callable objectthat re- turns a callable object with the same number of arguments as F:def F(arg):...F = decorator(F) # Rebind function name to decorator resultF(99) # Essentially calls decorator(F)(99) Copyright 2009 Trend Micro Inc.
    8. 8. ImplementationA decorator itself is a callable that returns a callable.def decorator(F): print ‖init decorator" return F@decoratordef func(): print "Hello‖This decorator is invoked at decoration time, and the callable it returns isinvoked when the original function name is later called. Copyright 2009 Trend Micro Inc.
    9. 9. Implementationthe decorator returns a wrapper that retains the original function in an enclosing scopedef decorator(F): def wrapper(*args): print ‖run function" F() return wrapper@decoratordef func(): print "Hello"When the name func is later called, it really invokes the wrapper functionreturned by decorator; the wrapper function can then run the original funcbecause it is still available in an enclosing scope. Copyright 2009 Trend Micro Inc.
    10. 10. ImplementationTo do the same with classes, we can overload the call operation and use instance at-tributes instead of enclosing scopes:class decorator: def __init__(self, func): # On @ decoration self.func = func print "init decorator‖ def __call__(self, *args): # On wrapped function call print "run function" self.func(*args)@decoratordef func(): # func = decorator(func), then func is passed to __init__ print "Hello‖func() #call __call__s *args Copyright 2009 Trend Micro Inc.
    11. 11. Class Decorators Classification 4/3/2012 Copyright 2009 Trend Micro Inc. 11
    12. 12. Usage@decorator # Decorateclass C:...x = C(99) # Make an instanceis equivalent to the following—the class is automatically passed to thedecorator func- tion, and the decorator’s result is assigned back to the classname:class C:...C = decorator(C) # Rebind class name to decorator resultx = C(99) # Essentially calls decorator(C)(99) Copyright 2009 Trend Micro Inc.
    13. 13. ImplementationA decorator itself is a callable that returns a callable.def decorator(C): print ‖init decorator" return C@decoratorclass C: def __init__(self, x, y): self.attr = spam’ Copyright 2009 Trend Micro Inc.
    14. 14. Implementationthe decorator returns a wrapper that retains the original function in an enclosing scopedef decorator(cls): class Wrapper: def __init__(self, *args): self.wrapped = cls(*args) def __getattr__(self, name): return getattr(self.wrapped, name) return Wrapper@decoratorclass C: def __init__(self, x, y): self.attr = spam’It does support multiple instances, because each instance creation call makes a newindependent wrapper object. Copyright 2009 Trend Micro Inc.
    15. 15. Implementation - invalidclass Decorator: def __init__(self, C): # On @ decoration self.C = C def __call__(self, *args): # On instance creation self.wrapped = self.C(*args) return self def __getattr__(self, attrname): # On atrribute fetch return getattr(self.wrapped, attrname)@decoratorclass C: ….x = C()y = C() # Overwrites x!this version fails to handle multiple instances of a given class—each instancecreation call overwrites the prior saved instance. Copyright 2009 Trend Micro Inc.
    16. 16. Decorator Nesting Classification 4/3/2012 Copyright 2009 Trend Micro Inc. 16
    17. 17. UsageSometimes one decorator isn’t enough. To support multiple steps of augmentation,decorator syntax allows you to add multiple layers of wrapper logic to a decoratedfunction or method. When this feature is used, each decorator must appear on a line ofits own. Decorator syntax of this form:@A@B@Cdef f(...): ...runs the same as the following:def f(...): ...f = A(B(C(f))) Copyright 2009 Trend Micro Inc.
    18. 18. Implementationdef sidedish1(meal): return lambda: meal() + 30def sidedish2(meal): return lambda: meal() + 40@sidedish1@sidedish2def friedchicken(): return 49.0is equivalent tofriedchicken = sidedish1(sidedish2(friedchicken))print(friedchicken()) # 119.0 Copyright 2009 Trend Micro Inc.
    19. 19. Decorator Arguments Classification 4/3/2012 Copyright 2009 Trend Micro Inc. 19
    20. 20. UsageBoth function and class decorators can also seem to take arguments, although reallythese arguments are passed to a callable that in effect returns the decorator, which inturn returns a callable. The following, for instance:@decorator(A, B)def F(arg): ...F(99)is automatically mapped into this equivalent form, where decorator is a callable thatreturns the actual decorator. The returned decorator in turn returns the callable run laterfor calls to the original function name:def F(arg): ...F = decorator(A, B)(F) # Rebind F to result of decorators return valueF(99) # Essentially calls decorator(A, B)(F)(99) Copyright 2009 Trend Micro Inc.
    21. 21. Implementationdef sidedish(number): return { 1 : lambda meal: (lambda: meal() + 30), 2 : lambda meal: (lambda: meal() + 40), 3 : lambda meal: (lambda: meal() + 50), 4 : lambda meal: (lambda: meal() + 60) }.get(number, lambda meal: (lambda: meal()))@sidedish(2)@sidedish(3)def friedchicken(): return 49.0print(friedchicken()) # 139.0 Copyright 2009 Trend Micro Inc.
    22. 22. def sidedish(number):Implementation def dish1(meal): return lambda: meal() + 30@sidedish(2) def dish2(meal):@sidedish(3) return lambda: meal() + 40def friedchicken(): return 49.0 def dish3(meal):print(friedchicken()) # 139.0 return lambda: meal() + 50 def dish4(meal): return lambda: meal() + 60 def nodish(meal): return lambda: meal() return { 1 : dish1, 2 : dish2, 3 : dish3, 4 : dish4 }.get(number, nodish) Copyright 2009 Trend Micro Inc.
    23. 23. Decorators Manage Functions and Classesdef decorate(O): # Save or augment function or class O return O@decoratordef F(): ... # F = decorator(F)@decoratorclass C: ... # C = decorator(C) Copyright 2009 Trend Micro Inc.
    24. 24. Logging example >>> spam(1, 2, 3)class tracer: call 1 to spam def __init__(self, func): 6 self.calls = 0 >>> spam (a, b, c) self.func = func call 2 to spam def __call__(self, *args): abc self.calls += 1 >>> spam.calls print(call %s to %s % (self.calls, self.func.__name__)) 2 self.func(*args)@tracerdef spam(a, b, c): print(a + b + c) Copyright 2009 Trend Micro Inc.
    25. 25. State Information Classification 4/3/2012 Copyright 2009 Trend Micro Inc. 25
    26. 26. Class instance attributes >>>spam(1, 2, 3)class tracer: def __init__(self, func): call 1 to spam self.calls = 0 6 self.func = func >>>spam(a=4, b=5, c=6) def __call__(self, *args, **kwargs): call 2 to spam self.calls += 1 15 print(call %s to %s % (self.calls, self.func.__name__)) >>>eggs(2, 16) return self.func(*args, **kwargs) call 1 to eggs@tracer 65536def spam(a, b, c): >>>eggs(4, y=4) print(a + b + c) call 2 to eggs 256@tracerdef eggs(x, y): print(x ** y) Copyright 2009 Trend Micro Inc.
    27. 27. Enclosing scopes and nonlocals >>>spam(1, 2, 3)def tracer(func): call 1 to spam calls = 0 6 def wrapper(*args, **kwargs): >>>spam(a=4, b=5, c=6) nonlocal calls call 2 to spam calls += 1 15 print(call %s to %s % (calls, func.__name__)) >>>eggs(2, 16) return func(*args, **kwargs) call 1 to eggs return wrapper 65536 >>>eggs(4, y=4) call 2 to eggs 256 Copyright 2009 Trend Micro Inc.
    28. 28. Enclosing scopes and globalscalls = 0 >>>spam(1, 2, 3)def tracer(func): call 1 to spam def wrapper(*args, **kwargs): 6 global calls >>>spam(a=4, b=5, c=6) calls += 1 call 2 to spam print(call %s to %s % (calls, func.__name__)) 15 return func(*args, **kwargs) >>>eggs(2, 16) return wrapper call 3 to eggs 65536 >>>eggs(4, y=4) call 4 to eggs 256 Copyright 2009 Trend Micro Inc.
    29. 29. Decorating Class Methods Classification 4/3/2012 Copyright 2009 Trend Micro Inc. 29
    30. 30. Class Blunders I - Decorating Class Methodsclass tracer: def __init__(self, func): self.calls = 0 self.func = func def __call__(self, *args, **kwargs): self.calls += 1 print(call %s to %s % (self.calls, self.func.__name__)) return self.func(*args, **kwargs) Copyright 2009 Trend Micro Inc.
    31. 31. Class Blunders I - Decorating Class Methodsclass Person: def __init__(self, name, pay): self.name = name self.pay = pay @tracer def giveRaise(self, percent): self.pay *= (1.0 + percent) @tracer def lastName(self): return self.name.split()[-1]bob = Person(Bob Smith, 50000)bob.giveRaise(.25) ## Runs tracer.__call__(???, .25)The root of the problem here is in the self argument of the tracer class’s __call__ method,is it a tracer instance or a Person instance? Copyright 2009 Trend Micro Inc.
    32. 32. Using nested functions to decorate methodsdef tracer(func): calls = 0 def onCall(*args, **kwargs): nonlocal calls calls += 1 print(call %s to %s % (calls, func.__name__)) return func(*args, **kwargs) return onCall Copyright 2009 Trend Micro Inc.
    33. 33. Singleton Classesinstances = {}def getInstance(aClass, *args): if aClass not in instances: instances[aClass] = aClass(*args) return instances[aClass]def singleton(aClass): def onCall(*args): return getInstance(aClass, *args) return onCall@singletonclass Spam: def __init__(self, val): self.attr = val Copyright 2009 Trend Micro Inc.
    34. 34. Why Decorators? (Revisited)drawbacks:• Type changes – As we’ve seen, when wrappers are inserted, a decorated function or class does not retain its original type—its name is rebound to a wrapper object.• Extra calls – A wrapping layer added by decoration incurs the additional performance cost of an extra call each time the decorated object is invoked Copyright 2009 Trend Micro Inc.
    35. 35. Why Decorators? (Revisited)benefits:• Explicit syntax – Decorators make augmentation explicit and obvious.• Code maintenance – Decorators avoid repeated augmentation code at each function or class call.• Consistency – Decorators make it less likely that a programmer will forget to use required wrapping logic. Copyright 2009 Trend Micro Inc.
    36. 36. Q&A Classification 4/3/2012 Copyright 2009 Trend Micro Inc. 36

    ×