Upgrade to Pro — share decks privately, control downloads, hide ads and more …

python decorators

python decorators

An easy introduction to python decorators (this deck is mostly code examples). Presented to the Memphis Python User group, December 2014.

Brad Montgomery

December 15, 2014
Tweet

More Decks by Brad Montgomery

Other Decks in Programming

Transcript

  1. class MyThing: @property def value(self): return self.calculate_value() def calculate_value(self): #

    do some calculations here >>> thing = MyThing() >>> thing.value 42
  2. class MyThing: @property def value(self): return self.calculate_value() def calculate_value(self): #

    do some calculations here >>> thing = MyThing() >>> thing.value 42
  3. class MyThing: @property def value(self): return self.calculate_value() def calculate_value(self): #

    do some calculations here >>> thing = MyThing() >>> thing.value 42
  4. # Generalized arguments def foo(*args, **kwargs): print("args: {}".format(args)) print("kwargs: {}".format(kwargs))

    >>> foo('a single arg') # args: ('a single arg',) # kwargs: {} >>> foo('arg1', 'arg2', kw1='keyword1', kw2='keyword2') # args: ('arg1', 'arg2') # kwargs: {'kw1': ‘keyword1', ‘kw2': 'keyword2'}
  5. # You can put functions inside of functions? def outside():

    name = "i'm outside!" def inside(): print(name) inside()
  6. # You can put functions inside of functions? def outside():

    name = "i'm outside!" def inside(): print(name) inside() >>> outside() # i'm outside!
  7. # Functions are fist-class citizens? def citizen_func(): pass >>> citizen_func.__class__

    # '<class 'function'>' >>> issubclass(citizen_func.__class__, object) # True
  8. # Functions are fist-class citizens? def citizen_func(): pass >>> citizen_func.__class__

    # '<class 'function'>' >>> issubclass(citizen_func.__class__, object) # True
  9. # Functions are fist-class citizens? def citizen_func(): pass >>> citizen_func.__class__

    # '<class 'function'>' >>> issubclass(citizen_func.__class__, object) # True
  10. # You can pass functions as # arguments to functions:

    def callme(msg): print(msg) def carly(f, y): f(y) carly(callme, "maybe") # maybe
  11. # You can pass functions as # arguments to functions:

    def callme(msg): print(msg) def carly(f, y): f(y) carly(callme, "maybe") # maybe
  12. # You can pass functions as # arguments to functions:

    def callme(msg): print(msg) def carly(f, y): f(y) carly(callme, "maybe") # maybe
  13. # You can pass functions as # arguments to functions:

    def callme(msg): print(msg) def carly(f, y): f(y) carly(callme, "maybe") # maybe
  14. # A Holiday decorator! def holiday(f): def wrapper(*args, **kwargs): result

    = f(*args, **kwargs) return "‚ {0} ‚".format(result) return wrapper
  15. # A Holiday decorator! def holiday(f): def wrapper(*args, **kwargs): result

    = f(*args, **kwargs) return "‚ {0} ‚".format(result) return wrapper @holiday def happy(): return "Happy Holidays!” # >>> happy() # “‚ Happy Holidays! ‚”
  16. # A Holiday decorator! def holiday(f): def wrapper(*args, **kwargs): result

    = f(*args, **kwargs) return "‚ {0} ‚".format(result) return wrapper @holiday def happy(): return "Happy Holidays!” # >>> happy() # “‚ Happy Holidays! ‚”
  17. # A simple, DIY timing decorator import time def timed(func):

    """Decorator that prints time info.""" def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(func.__name__, end - start) return result return wrapper
  18. # A simple, DIY timing decorator import time def timed(func):

    """Decorator that prints time info.""" def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(func.__name__, end - start) return result return wrapper
  19. # A simple, DIY timing decorator import time def timed(func):

    """Decorator that prints time info.""" def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(func.__name__, end - start) return result return wrapper
  20. # A simple, DIY timing decorator import time def timed(func):

    """Decorator that prints time info.""" def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(func.__name__, end - start) return result return wrapper
  21. # Or using the old-style syntax. def foo(): """A Foo

    function.""" print("Foo.") old_foo = timed(old_foo)
  22. # Or using the old-style syntax. def foo(): """A Foo

    function.""" print("Foo.") foo = timed(foo)
  23. # Use functools.wraps to keep that meta-data! from functools import

    wraps def timed(func): """Better timing decorator.""" @wraps(func) def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(func.__name__, end - start) return result return wrapper
  24. # Use functools.wraps to keep that meta-data! from functools import

    wraps def timed(func): """Better timing decorator.""" @wraps(func) def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(func.__name__, end - start) return result return wrapper
  25. # Better! >>> foo.__name__ # Expected: 'foo' >>> foo.__doc__ #

    Should show docstring # AND! The original function is # in __wrapped__ >>> foo.__wrapped__()
  26. # A Practical Example. Logging function Calls. import time def

    logged(func): @wraps(func) def wrapper(*args, **kwargs): result = func(*args, **kwargs) msg = "[{}] {} called with args: {}, kwargs: {}" print(msg.format( time.ctime(), func.__name__, args, kwargs )) return result return wrapper
  27. @logged def super_funk(*args, **kwargs): pass >>> super_funk('Blammo', kap='pow') # [Mon

    Nov 17 16:38:44 2014] super_funk called # with args: ('Blammo',), kwargs: {'kap': 'pow'}
  28. from decimal import Decimal def currency(func): @wraps(func) def wrapper(*args, **kwargs):

    result = func(*args, **kwargs) result = Decimal(str(result)) result = result.quantize(Decimal(".01")) return "$ {0}".format(result) return wrapper
  29. # Decorators with arguments def currency(symbol='$'): def decorate(func): @wraps(func) def

    wrapper(*args, **kwargs): result = func(*args, **kwargs) result = Decimal(str(result)) result = result.quantize(Decimal(".01")) return "{} {}".format(symbol, result) return wrapper return decorate
  30. # Decorators with arguments def currency(symbol='$'): def decorate(func): @wraps(func) def

    wrapper(*args, **kwargs): result = func(*args, **kwargs) result = Decimal(str(result)) result = result.quantize(Decimal(".01")) return "{} {}".format(symbol, result) return wrapper return decorate
  31. # Decorators with arguments def currency(symbol='$'): def decorate(func): @wraps(func) def

    wrapper(*args, **kwargs): result = func(*args, **kwargs) result = Decimal(str(result)) result = result.quantize(Decimal(".01")) return "{} {}".format(symbol, result) return wrapper return decorate Take it another level!
  32. # Decorators with arguments def currency(symbol='$'): def decorate(func): @wraps(func) def

    wrapper(*args, **kwargs): result = func(*args, **kwargs) result = Decimal(str(result)) result = result.quantize(Decimal(".01")) return "{} {}".format(symbol, result) return wrapper return decorate
  33. Resources # Python Cookbook, 3rd ed. # Chapter 9, Metaprogramming

    http://www.jeffknupp.com/blog/2013/11/29/improve-your-python-decorators-explained/ http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/ https://realpython.com/blog/python/primer-on-python-decorators/ http://python-3-patterns-idioms-test.readthedocs.org/en/latest/PythonDecorators.html