decorators explained: a powerful tool that should be in your python toolbelt

44
Decorators Explained A Powerful Tool That Should Be in Your Python Toolbelt

Upload: samuel-fortier-galarneau

Post on 13-Jun-2015

247 views

Category:

Technology


2 download

DESCRIPTION

Learn what Python decorators are, how they are implemented, and when you should turn to them to simplify your solutions.

TRANSCRIPT

Page 1: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

Decorators Explained

A Powerful Tool That Should Be in Your Python Toolbelt

Page 2: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

Samuel Fortier-Galarneau

Software Developer @ Digium, Inc@samuelg

http://github.com/samuelg

Page 3: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt
Page 4: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

Agenda

• Primer

• What is a decorator?

• How do they work?

• Examples

Page 5: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

PrimerFunctions

• Functions in Python are first class members

• They can be passed to other functions and return new functions

def add(first, second): return first + second

def partial(func, first): def wrapper(second): return func(first, second) return wrapper

Page 6: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

PrimerFunctions

>>> func = partial(add, 1)>>> func(2)3

Page 7: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

PrimerVariable function arguments

• Functions can have variable positional and keyword arguments via *args and **kwargs

• *args and **kwargs can be packed again and passed along to another function

def func(*args, **kwargs): print args print kwargs

Page 8: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

PrimerVariable function arguments

>>> func(1, 2, my_arg=‘value')(1, 2){'my_arg': 'value'}

Page 9: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

PrimerVariable function arguments

def func(*args, **kwargs): other_func(*args, **kwargs)

def other_func(arg1, arg2, arg3): print arg1 print arg2 print arg3

Page 10: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

PrimerVariable function arguments

>>> func(1, 2, arg3=3)123

Page 11: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

What is a decorator?

• Injects code before/after a function call or object creation

• A callable wrapper around a callable resource (object that implements __call__ is callable)

• Similar to macros in C/C++ but uses built in Python syntax and language semantics

Page 12: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

What is a decorator?• Can be implemented as a function or a

class

• Can be applied to a function or a class

• Functions have an implicit __call__ method which makes them callable

• Classes must implement a __call__ method to qualify as a decorator

Page 13: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function

def decorator(func): def wrapper(): print 'in wrapper()' return func() return wrapper

@decoratordef my_function(): print 'in my_function()'

Page 14: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function

>>> my_function()in wrapper()in my_function()

Page 15: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function

def decorator(func): def wrapper(): print 'in wrapper()' return func() return wrapper

@decoratordef my_function(): print 'in my_function()'

Page 16: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function

def decorator(func): def wrapper(): print 'in wrapper()' return func() return wrapper

@decoratordef my_function(): print 'in my_function()'

Page 17: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function

def decorator(func): def wrapper(): print 'in wrapper()' return func() return wrapper

@decoratordef my_function(): print 'in my_function()'

Page 18: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function

def decorator(func): def wrapper(): print 'in wrapper()' return func() return wrapper

@decoratordef my_function(): print 'in my_function()'

Page 19: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function

wrapper is a closure over func

def decorator(func): def wrapper(): print 'in wrapper()' return func() return wrapper

@decoratordef my_function(): print 'in my_function()'

Page 20: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function

def decorator(func): def wrapper(): print 'in wrapper()' return func() return wrapper

@decoratordef my_function(): print 'in my_function()'

Page 21: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator class

class Decorator(object): def __init__(self, func): self.func = func def __call__(self): print 'in __call__()' return self.func()

@Decoratordef my_function(): print 'in my_function()'

Page 22: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator class

>>> my_function()in __call__()in my_function()

Page 23: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function with

argumentsdef decorator(*args, **kwargs): def receiver(func): def wrapper(): print 'in wrapper()' print args print kwargs return func() return wrapper return receiver

@decorator(1, config='value')def my_function(): print 'in my_function()'

Page 24: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function with

arguments

>>> my_function()in wrapper()(1,){'config': 'value'}in my_function()

Page 25: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function with

argumentsdef decorator(*args, **kwargs): def receiver(func): def wrapper(): print 'in wrapper()' print args print kwargs return func() return wrapper return receiver

@decorator(1, config='value')def my_function(): print 'in my_function()'

Page 26: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function with

argumentsdef decorator(*args, **kwargs): def receiver(func): def wrapper(): print 'in wrapper()' print args print kwargs return func() return wrapper return receiver

@decorator(1, config='value')def my_function(): print 'in my_function()'

Page 27: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function with

argumentsdef decorator(*args, **kwargs): def receiver(func): def wrapper(): print 'in wrapper()' print args print kwargs return func() return wrapper return receiver

@decorator(1, config='value')def my_function(): print 'in my_function()'

Page 28: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function with

argumentsdef decorator(*args, **kwargs): def receiver(func): def wrapper(): print 'in wrapper()' print args print kwargs return func() return wrapper return receiver

@decorator(1, config='value')def my_function(): print 'in my_function()'

Page 29: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function with

argumentsdef decorator(*args, **kwargs): def receiver(func): def wrapper(): print 'in wrapper()' print args print kwargs return func() return wrapper return receiver

@decorator(1, config='value')def my_function(): print 'in my_function()'

Page 30: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function with

argumentsdef decorator(*args, **kwargs): def receiver(func): def wrapper(): print 'in wrapper()' print args print kwargs return func() return wrapper return receiver

@decorator(1, config='value')def my_function(): print 'in my_function()'

Page 31: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function with

argumentsdef decorator(*args, **kwargs): def receiver(func): def wrapper(): print 'in wrapper()' print args print kwargs return func() return wrapper return receiver

@decorator(1, config='value')def my_function(): print 'in my_function()'

Page 32: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator function with

argumentsdef decorator(*args, **kwargs): def receiver(func): def wrapper(): print 'in wrapper()' print args print kwargs return func() return wrapper return receiver

@decorator(1, config='value')def my_function(): print 'in my_function()'

Page 33: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator class with

argumentsclass Decorator(object): def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs def __call__(self, func): def wrapper(): print 'in __call__()' print self.args print self.kwargs return func() return wrapper

@Decorator(1, config='value')def my_function(): print 'in my_function()'

Page 34: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

How do they work?Decorator class with

arguments

>>> my_function()in __call__()(1,){'config': 'value'}in my_function()

Page 35: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

Example: counting function calls

class Counter(object): def __init__(self, func): self.count = 0 self.func = func def __call__(self): self.count += 1 return self.func() def getCount(self): return self.count

@Counterdef function(): pass

Page 36: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

Example: counting function calls

>>> function()>>> function()>>> function()>>> function.getCount()3

Page 37: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

Example: memoizeclass Memoize(object): def __init__(self, func): self.cache = {} self.func = func def __call__(self, *args, **kwargs): cache_key = '%s%s' % (args, kwargs) if cache_key in self.cache: return self.cache[cache_key] else: result = self.func(*args, **kwargs) self.cache[cache_key] = result return result

@Memoizedef function(bound): sum = 0 for x in range(bound): sum += x return sum

Page 38: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

Example: memoize

>>> function(100000000)~ 11 seconds>>> function(100000000)~ 10 milliseconds

Page 39: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

Example: type checker

def type_check(*types): def receiver(func): def wrapper(*args, **kwargs): for index, type_arg in enumerate(types): if type(args[index]) != type_arg: raise TypeError('Expected %s at index %s' % (type_arg, index)) return func(*args, **kwargs) return wrapper return receiver

@type_check(int, int, str)def function(first, second, third): print 'success'

Page 40: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

Example: type checker

>>> function(1, 2, ‘works')success>>> function('will', 'not', ‘work')Traceback (most recent call last): File "type.py", line 17, in <module> function('will', 'not', 'work') File "type.py", line 7, in wrapper raise TypeError('Expected %s at index %s' % (type_arg, index))TypeError: Expected <type 'int'> at index 0

Page 41: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

Example: singleton

instances = {}def singleton(cls): def create(*args): if cls not in instances: instances[cls] = cls(*args) return instances[cls] return create

@singletonclass Toolbelt(object): def __init__(self): pass

Page 42: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

Example: singleton

>>> toolbelt = Toolbelt()>>> another = Toolbelt()>>> another == toolbeltTrue

Page 43: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

Other uses

• Retries

• Pre/Post conditions

• Function input/output transforms

• Performance logging

• Authorization

Page 44: Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt

Thanks!

Samuel Fortier-Galarneau@samuelg

http://github.com/samuelghttp://www.slideshare.net/sgalarne/decorators-2