0% found this document useful (0 votes)
72 views57 pages

Closures and Decorators Slides

Download as pdf or txt
Download as pdf or txt
Download as pdf or txt
You are on page 1/ 57

Closures and Decorators

Austin Bingham Robert Smallshire


@austin_bingham @robsmallshire
[email protected] [email protected]
Presenter
def
define new functions, executed at runtime
Local functions

def func():
x = 1
y = 2
return x + y
Local functions

def func(): def func():


x = 1 def local_func():
y = 2 a = 'hello, '
return x + y b = 'world'
return a + b
!
x = 1
y = 2
return x + y
Local functions

def func(): def func():


x = 1 def local_func():
y = 2 a = 'hello, '
return x + y b = 'world'
return a + b
!
x = 1
y = 2
return x + y
LEGB rule
local, enclosing, global, built-in
LEGB Rule

PI = TAU / 2
!
def func(x):
def local_func(n):
a = 'hello, '
return a + n
!
y = 2
return x + y

module.py
LEGB Rule

PI = TAU / 2
!
def func(x):
def local_func(n):
a = 'hello, '
return a + n
!
y = 2
return x + y

module.py
LEGB Rule

PI = TAU / 2
!
def func(x):
def local_func(n):
a = 'hello, '
return a + n
!
y = 2
return x + y

module.py
LEGB Rule

PI = TAU / 2
!
def func(x):
def local_func(n):
a = 'hello, '
return a + n
!
y = 2
return x + y

module.py
Local functions

•Useful for specialized, one-off functions


•Aid in code organization and readability
•Similar to lambdas, but more general
• May contain multiple expressions

• May contain statements


Returning functions

def outer():
def inner():
print('inner')
inner()
Returning functions

def outer():
def inner():
print('inner')
return inner
!
i = outer()
i()
First-class
Functions
functions can be treated like any other object
Closures

def outer():
x = 3
!
def inner(y):
return x + y
!
return inner
!
i = outer()
Closures

def outer():
x = 3
!
def inner(y):
return x + y
!
return inner
!
i = outer()
Closures

def outer():
x = 3
!
def inner(y):
return x + y
!
return inner
!
i = outer()
Closures

!
x = 3
?
def inner(y):
return x + y
!

!
i = outer()
Closures
maintain references to objects from earlier scopes
Function
factory
function that returns new, specialized functions
!

LEGB does not apply when


making new bindings.
global
introduce names from global namespace
into the local namespace
nonlocal
introduce names from the enclosing namespace
into the local namespace
E r r o r
y n t a x
t a S s n ’ t
ge d o e
You e name
f th
i t.
ex is
decorators
modify or enhance functions without changing their definition

@my_decorator
def my_function():
. . .
Decorators

@my_decorator!
def my_function(x, y):!
return x + y
Decorators

@my_decorator!
def my_function(x, y):! FUNCTION
OBJECT
return x + y
Decorators

def my_decorator(f):!
y):! FUNCTION . . .!
OBJECT
return new_f
Decorators

def my_decorator(f):!
FUNCTION FUNCTION
. . .!
OBJECT OBJECT
return new_f
Decorators

@my_decorator!
FUNCTION
def my_function(x, y):!
OBJECT
return x + y
Decorators

def my_function(x, y):!


!
! FUNCTION
! OBJECT
!
Decorators

•Replace, enhance, or modify existing functions


•Does not change the original function definition
•Calling code does not need to change
•Decorator mechanism uses the modified
function’s original name
Decorators

def vegetable():
return 'blomkål'
!
def animal():
return 'bjørn'
!
def mineral():
return 'stål'
Decorators

def vegetable():
return ascii(‘blomkål’)
!
def animal():
return ascii(‘bjørn’)
!
def mineral():
return ascii(‘stål’)
Decorators

def vegetable():
return ascii(‘blomkål’)
!
def animal():
return ascii(‘bjørn’)
!
def mineral():
return ascii(‘stål’)

Not
very
le. main
alab tain
y sc able.
ver
Not
Decorators

We’ve seen functions as


decorators…
!

…but other objects can be


decorators as well.
Classes as decorators

class MyDec:!
def __init__(self, f):!
@MyDec!
! . . .!
def func():!
!
. . .
def __call__(self):!
! . . .
Classes as decorators

Classes are
callable…

class MyDec:!
def __init__(self, f):!
@MyDec!
! . . .!
def func():!
!
. . .
def __call__(self):!
! . . .
Classes as decorators

Classes are …so they can be


callable… used as decorators.

class MyDec:!
def __init__(self, f):!
@MyDec!
! . . .!
def func():!
!
. . .
def __call__(self):!
! . . .
Classes as decorators
Applying a class
decorator creates a
new instance…

class MyDec:!
def __init__(self, f):!
@MyDec!
! . . .!
def func():!
!
. . .
def __call__(self):!
! . . .
Classes as decorators
Applying a class
decorator creates a
new instance…

class MyDec:!
def __init__(self, f):!
@MyDec!
! . . .!
def func():!
!
. . .
def __call__(self):!
! . . .

…so the instance


must be callable.
Instances as decorators

Decorating with
an instance calls
the instance.

class AnotherDec:!
def __call__(self, f):! @AnotherDec()!
! def wrap():! def func():!
! . . .! . . .
! return wrap
Instances as decorators
Decorating
with an
instance calls
the instance.

class AnotherDec:!
def __call__(self, f):! @AnotherDec()!
! def wrap():! def func():!
! . . .! . . .
! return wrap

The return value of


__call__ must be
callable.
Multiple decorators

@decorator1
@decorator2
@decorator3
def some_function():
. . .
Multiple decorators

@decorator1
@decorator2
@decorator3
def some_function():
. . .
Multiple decorators

@decorator1
@decorator2
def some_function():
. . .
!
Multiple decorators

@decorator1
@decorator2
def some_function():
. . .
!
Multiple decorators

@decorator1
def some_function():
. . .
!
!
Multiple decorators

@decorator1
def some_function():
. . .
!
!
Multiple decorators

def some_function():
. . .
!
!
!
functools.wrap()

Naive decorators can lose


important metadata.
functools.wraps()
properly update metadata on wrapped functions
Decorators

•Decorators are a powerful tool


•Decorators are widely used in Python
•It’s possible to overuse decorators; be mindful
•They can improve maintainability, increase
clarity, and reduce complexity
Duck Tails
Factory functions which produce
decorator functions which make
wrapper functions which wrap functions
(with added closures!)
Duck Tails
Whew!
Closures and Decorators
def function(a, b):
print("A function")

def local_function(x, y):


print("A local function")
return x * y + a * b

return local_function

>>> p = function(5, 7)
A function
>>> p
<function function.<locals>.local_function at 0x10299f320>
>>> p.__closure__
(<cell at 0x1029bc8d8: int object at 0x100233120>,
<cell at 0x1029bc980: int object at 0x100233160>)
Closures and Decorators
def log_to(stream):

def logging_decorator( func):

@wraps(func)
def logging_wrapper(*args, **kwargs):
print(func.__name__ + " was called" , file=stream)
return func(*args, **kwargs)

return logging_wrapper

return logging_decorator

@second_decorator @log_to(sys.stderr)
@logging_decorator
def another_function(x):
def some_function(x):
"""A decorated function"""
"""A decorated function"""
return x + x
return x * x

>>> print(some_function.__name__)
some_function
>>> print(some_function.__doc__)
A decorated function

You might also like