Share
Embed
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
Transcript
51 slides
-
# An example from Django @login_required def some_view(request): # ...
return HttpResponse(content) -
# An example from Django @login_required def some_view(request): # ...
return HttpResponse(content) -
class MyThing: @property def value(self): return self.calculate_value() def calculate_value(self): #
do some calculations here >>> thing = MyThing() >>> thing.value 42 -
class MyThing: @property def value(self): return self.calculate_value() def calculate_value(self): #
do some calculations here >>> thing = MyThing() >>> thing.value 42 -
class MyThing: @property def value(self): return self.calculate_value() def calculate_value(self): #
do some calculations here >>> thing = MyThing() >>> thing.value 42 -
# Generalized arguments def foo(*args, **kwargs): print("args: {}".format(args)) print("kwargs: {}".format(kwargs))
>>> foo('a single arg') # args: ('a single arg',) # kwargs: {} -
# 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'} -
# You can put functions inside of functions? def outside():
name = "i'm outside!" def inside(): print(name) inside() -
# You can put functions inside of functions? def outside():
name = "i'm outside!" def inside(): print(name) inside() >>> outside() # i'm outside! -
# Functions are fist-class citizens? def citizen_func(): pass >>> citizen_func.__class__
# '<class 'function'>' >>> issubclass(citizen_func.__class__, object) # True -
# Functions are fist-class citizens? def citizen_func(): pass >>> citizen_func.__class__
# '<class 'function'>' >>> issubclass(citizen_func.__class__, object) # True -
# Functions are fist-class citizens? def citizen_func(): pass >>> citizen_func.__class__
# '<class 'function'>' >>> issubclass(citizen_func.__class__, object) # True -
# You can pass functions as # arguments to functions:
def callme(msg): print(msg) def carly(f, y): f(y) carly(callme, "maybe") # maybe -
# You can pass functions as # arguments to functions:
def callme(msg): print(msg) def carly(f, y): f(y) carly(callme, "maybe") # maybe -
# You can pass functions as # arguments to functions:
def callme(msg): print(msg) def carly(f, y): f(y) carly(callme, "maybe") # maybe -
# You can pass functions as # arguments to functions:
def callme(msg): print(msg) def carly(f, y): f(y) carly(callme, "maybe") # maybe -
# A Holiday decorator! def holiday(f): def wrapper(*args, **kwargs): result
= f(*args, **kwargs) return "‚ {0} ‚".format(result) return wrapper -
# 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! ‚” -
# 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! ‚” -
# 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 -
# 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 -
# 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 -
# 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 -
# Or using the old-style syntax. def foo(): """A Foo
function.""" print("Foo.") old_foo = timed(old_foo) -
# Or using the old-style syntax. def foo(): """A Foo
function.""" print("Foo.") foo = timed(foo) -
# Problems! >>> foo.__name__ # Expected: 'foo' >>> foo.__doc__ #
Should show docstring -
# Problems! >>> foo.__name__ # Expected: 'foo' >>> foo.__doc__ #
Should show docstring wrapper None -
# 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 -
# 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 -
# Better! >>> foo.__name__ # Expected: 'foo' >>> foo.__doc__ #
Should show docstring # AND! The original function is # in __wrapped__ >>> foo.__wrapped__() -
# 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 -
@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'} -
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 -
# 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 -
# 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 -
# 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! -
# 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 -
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