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

description

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

Transcript of Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt.

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