SlideShare a Scribd company logo
1 of 65
Download to read offline
Python  DECORATORS  
DEMYSTIFIED	

{

“event”:      “PyCon  ES  2013”	
“author”:  “Pablo  Enfedaque”	
“twi4er”:  “pablitoev56”
Do  you  know  what’s  happening  each  
time  you  use  the  @    (at)  symbol  to  
decorate  a  function  or  class?	
	
Today  we  are  going  to  see  how  
Python’s  decorators  syntactic  sugar  
works  under  the  hood	

Welcome!	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
And  that’s  why  we  will  talk  about  
Python  namespaces  and  scopes	

Welcome!	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
And  finally  will  manually  implement  
and  apply  a  handcrafted  decorator	

Welcome!	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
Let’s  start  implementing  some	
useful  stuff  for  the  talk	

{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
from collections import OrderedDict	
	
CACHE = OrderedDict()	
MAX_SIZE = 100	
	
def set_key(key, value):	
"Set a key value, removing oldest key if MAX_SIZE exceeded"	
CACHE[key] = value	
if len(CACHE) > MAX_SIZE:	
CACHE.popitem(last=False)	
	
def get_key(key):	
"Retrieve a key value from the cache, or None if not found"	
return CACHE.get(key, None)	
	
	
>>> set_key("my_key", "the_value”)	
>>> print(get_key("my_key"))	
the_value	
	
>>> print(get_key("not_found_key"))	
None	

A  simple  software  cache	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
def factorial(n):	
"Return n! (the factorial of n): n! = n * (n-1)!"	
if n < 2:	
return 1	
return n * factorial(n - 1)	
	
	
>>> list(map(factorial, range(10)))	
[1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]	
	
	
	
def fibonacci(n):	
"Return nth fibonacci number: fib(n) = fib(n-1) + fib(n-2)"	
if n < 2:	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	
	
	
>>> list(map(fibonacci, range(10)))	
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]	
	

Factorial  and  fibonacci  functions	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
Pre4y  easy,  right?	

{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
However…	

{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
from collections import OrderedDict	
	
CACHE = OrderedDict()	
MAX_SIZE = 100	
	
def set_key(key, value):	
"Set a key value, removing oldest key if MAX_SIZE exceeded"	
CACHE[key] = value	
if len(CACHE) > MAX_SIZE:	
CACHE.popitem(last=False)	
	
def get_key(key):	
"Retrieve a key value from the cache, or None if not found"	
return CACHE.get(key, None)	
	
	
>>> set_key("my_key", "the_value”)	
>>> print(get_key("my_key"))	
the_value	
	
>>> print(get_key("not_found_key"))	
None	

How  do  we  access  this  a4ribute?	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
def factorial(n):	
"Return n! (the factorial of n): n! = n * (n-1)!"	
if n < 2:	
return 1	
return n * factorial(n - 1)	
	
	
>>> list(map(factorial, range(10)))	
[1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]	
	
	
	
def fibonacci(n):	
"Return nth fibonacci number: fib(n) = fib(n-1) + fib(n-2)"	
if n < 2:	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	
	
	
>>> list(map(fibonacci, range(10)))	
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]	
	

How  are  recursive  calls  possible?	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
>>> from os import path	
	
>>> print(type(path), id(path))	
<class 'module'> 4300435112	
	
	
	
>>> from sys import path	
	
>>> print(type(path), id(path))	
<class 'list'> 4298480008	
	
	
	
def split_path(path, sep="/"):	
print(type(path), id(path))	
return path.split(sep)	
	
>>> split_path("/this/is/a/full/path")	
<class 'str'> 4302038120	
['', 'this', 'is', 'a', 'full', 'path']	
	

A  simpler  case	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
>>> from os import path	
	
>>> print(type(path), id(path))	
<class 'module'> 4300435112	
	
	
	
>>> from sys import path	
	
>>> print(type(path), id(path))	
<class 'list'> 4298480008	
	
	
	
def split_path(path, sep="/"):	
print(type(path), id(path))	
return path.split(sep)	
	
>>> split_path("/this/is/a/full/path")	
<class 'str'> 4302038120	
['', 'this', 'is', 'a', 'full', 'path']	
	

The  same  name  defined  several  times?	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
Let  me  introduce  Python  namespaces	

{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
A  namespace  is  a  mapping  
from  names  to  objects	

{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
> 

The  set  of  built-­‐‑in  names  (functions,  exceptions)	

> 

Global  names  in  a  module  (including  imports)	

> 

Local  names  in  a  function  invocation	

> 

Names  defined  in  top-­‐‑level  invocation  of  interpreter	

Python  namespaces  examples	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
There  is  no  relation  between  names  in  
different  namespaces	
	

Two  modules  or  functions  may  define  the  same  
name  without  confusion	

Python  namespaces	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
Namespaces  are  created  (and  deleted)  
at  different  moments  and  have  
different  lifetimes	

Python  namespaces	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
The  built-­‐‑ins  namespace  is  created  
when  the  Python  interpreter  starts	
	

And  is  never  deleted	

Python  namespaces  lifetimes	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
A  module  global  namespace  is  
created  when  the  module  definition  is  
read-­‐‑in  (when  it  is  imported)	
	

Normally  it  lasts  until  the  interpreter  quits	

Python  namespaces  lifetimes	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
A  function  local  namespace  is  created  
each  time  it  is  called	
	

It  is  deleted  when  the  function  returns  or  raises	

Python  namespaces  lifetimes	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
And  what  about  Python  scopes?	

{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
A  scope  is  a  textual  region  

of  a  program  where  a  
namespace  is  directly  
accessible	

{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
Scopes  are  determined  statically	
but  used  dynamically	

Python  scopes	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
At  any  time  during  execution,  there  
are  at  least  three  nested  scopes  whose  
namespaces  are  directly  accessible	

Python  scopes	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
1. 

The  innermost  scope  contains  the  local  
names	
> 

2. 

3. 

The  scopes  of  any  enclosing  functions,  
with  non-­‐‑local,  but  also  non-­‐‑global  names	

The  next-­‐‑to-­‐‑last  scope  contains  the  
current  module'ʹs  global  names	
The  outermost  scope  is  the  namespace  
containing  built-­‐‑in  names	

Python  nested  scopes	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
Names  are  searched  in  nested  scopes  
from  inside  out	
	

From  locals  to  built-­‐‑ins	

Python  nested  scopes	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
$ python	
Python 2.7.5 (default, Aug 25 2013, 00:04:04)	
[GCC 4.2.1 Compatible Apple LLVM 5.0
(clang-500.0.68)] on darwin	
Type "help", "copyright", "credits" or "license"
for more information.	
>>> import cache	
>>> cache.set_key("my_key", 7)	
>>> cache.get_key("my_key")	
7	
>>>	
"""	

Simple cache implementation	
"""	
from collections import OrderedDict	
	
CACHE = OrderedDict()	
MAX_SIZE = 100	
	
def set_key(key, value):	
"Set a key value, removing oldest key if MAX_SIZE exceeded"	
CACHE[key] = value	
if len(CACHE) > MAX_SIZE:	
CACHE.popitem(last=False)	
	
def get_key(key):	
"Retrieve a key value from the cache, or None if not found"	
return CACHE.get(key, None)	

Python  scopes	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
$ python	
Python 2.7.5 (default, Aug 25 2013, 00:04:04)	
[GCC 4.2.1 Compatible Apple LLVM 5.0
(clang-500.0.68)] on darwin	
Type "help", "copyright", "credits" or "license"
for more information.	
>>> import cache	
>>> cache.set_key("my_key", 7)	
>>> cache.get_key("my_key")	
7	
>>>	
"""	

The  outermost  scope:	
built-­‐‑in  names	

The  next-­‐‑to-­‐‑last  scope:	

Simple cache implementation	
current  module’s  global  names	
"""	
from collections import OrderedDict	
	
CACHE = OrderedDict()	
MAX_SIZE = 100	
	
def set_key(key, value):	
"Set a key value, removing oldest key if MAX_SIZE exceeded"	
CACHE[key] = value	
if len(CACHE) > MAX_SIZE:	
The  innermost  scope:	
CACHE.popitem(last=False)	
current  local  names	
	
def get_key(key):	
"Retrieve a key value from the cache, or None if not found"	
return CACHE.get(key, None)	

Python  scopes	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
def get_power_func(y):	
print("Creating function to raise to {}".format(y))	
	
def power_func(x):	
print("Calling to raise {} to power of {}".format(x, y))	
x = pow(x, y)	
return x	
	
return power_func	
	
	
>>> raise_to_4 = get_power_func(4)	
Creating function to raise to 3	
	
>>> x = 3	
>>> print(raise_to_4(x))	
Calling to raise 3 to power of 4	
81	
	
>>> print(x)	
3	

Another  more  complex  case	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
def get_power_func(y):	
print("Creating function to raise to {}".format(y))	
	
def power_func(x):	
print("Calling to raise {} to power of {}".format(x, y))	
x = pow(x, y)	
return x	
	
return power_func	
	
	
>>> raise_to_4 = get_power_func(4)	
Creating function to raise to 3	
	
>>> x = 3	
>>> print(raise_to_4(x))	
Calling to raise 3 to power of 4	
81	
	
>>> print(x)	
3	

Where  is  y  defined?	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
def get_power_func(y):	
print("Creating function to raise to {}".format(y))	
	
def power_func(x):	
print("Calling to raise {} to power of {}".format(x, y))	
x = pow(x, y)	
return x	
The  innermost  scope:  local  names	
	
return power_func	
	
Enclosing  function  scope:	
	
>>> raise_to_4 = get_power_func(4)	
non-­‐‑local  non-­‐‑global  names	
Creating function to raise to 3	
	
>>> x = 3	
>>> print(raise_to_4(x))	
Calling to raise 3 to power of 4	
81	
	
The  next-­‐‑to-­‐‑last  scope:	
>>> print(x)	
3	
current  module’s  global  names	

Nested  scopes	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
def get_power_func(y):	
print("Creating function to raise to {}".format(y))	
	
def power_func(x):	
print("Calling to raise {} to power of {}".format(x, y))	
x = pow(x, y)	
return x	
	
return power_func	
	
>>> raise_to_4 = get_power_func(4)	
Creating function to raise to 3	
	
>>> print(raise_to_4.__globals__)	
{'x': 3, 'raise_to_4': <function
get_power_func.<locals>.power_func at 0x100658488>,
'get_power_func': <function get_power_func at 0x1003b6048>, ...}	
	
	
>>> print(raise_to_4.__closure__)	
(<cell at 0x10065f048: int object at 0x10023b280>,)	

There  is  a  closure!	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
A  function  closure  is  a  
reference  to  each  of  the  non-­‐‑
local  variables  of  the  function	

{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
def get_power_func(y):	
print("Creating function to raise to {}".format(y))	
	
def power_func(x):	
print("Calling to raise {} to power of {}".format(x, y))	
x = pow(x, y)	
return x	
	
return power_func	
	
>>> raise_to_4 = get_power_func(4)	
Creating function to raise to 3	
	
>>> print(raise_to_4.__globals__)	
{'x': 3, 'raise_to_4': <function
get_power_func.<locals>.power_func at 0x100658488>,
'get_power_func': <function get_power_func at 0x1003b6048>, ...}	
	
	
>>> print(raise_to_4.__closure__)	
(<cell at 0x10065f048: int object at 0x10023b280>,)	

So,  where  is  y  defined?	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
Well,  maybe  you  are  wondering	
where  are  the  decorators  in  this  talk…	

{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
So,  let’s  manually  apply  a  decorator	

{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
def factorial(n):	
"Return n! (the factorial of n): n! = n * (n-1)!"	
if n < 2:	
return 1	
return n * factorial(n - 1)	
	
>>> start = time.time()	
>>> factorial(35)	
10333147966386144929666651337523200000000	
>>> print("Elapsed:", time.time() - start)	
Elapsed: 0.0007369518280029297	
	
def fibonacci(n):	
"Return nth fibonacci number: fib(n) = fib(n-1) + fib(n-2)"	
if n < 2:	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	
	
>>> start = time.time()	
>>> fibonacci(35)	
9227465	
>>> print("Elapsed:", time.time() - start)	
Elapsed: 6.916048049926758	

Let’s  go  back  to  these  functions	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
4	

3	
2	

5	

2	
1	

1	

1	

0	

0	
3	

2	

1	

1	

0	

fibonacci(5)  recursive  calls  graph	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
from collections import OrderedDict	
	
CACHE = OrderedDict()	
MAX_SIZE = 100	
	
def set_key(key, value):	
"Set a key value, removing oldest key if MAX_SIZE exceeded"	
CACHE[key] = value	
if len(CACHE) > MAX_SIZE:	
CACHE.popitem(last=False)	
	
def get_key(key):	
"Retrieve a key value from the cache, or None if not found"	
return CACHE.get(key, None)	
	
	
>>> set_key("my_key", "the_value”)	
>>> print(get_key("my_key"))	
the_value	
	
>>> print(get_key("not_found_key"))	
None	

Do  you  remember  we  have  a  cache?	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
import cache	
	
def fibonacci(n):	
"Return nth fibonacci number: fib(n) = fib(n-1) + fib(n-2)"	
if n < 2:	
return n	
fib = cache.get_key(n)	
if fib is None:	
fib = fibonacci(n - 1) + fibonacci(n - 2)	
cache.set_key(n, fib)	
return fib	
	
>>> start = time.time()	
>>> fibonacci(35)	
9227465	
>>> print("Elapsed:", time.time() - start)	
Elapsed: 0.0007810592651367188	
	
>>> start = time.time()	
>>> fibonacci(100)	
354224848179261915075	
>>> print("Elapsed:", time.time() - start)	
Elapsed: 0.0013179779052734375	

What  about  this  version?	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
import cache	
	
def fibonacci(n):	
"Return nth fibonacci number: fib(n) = fib(n-1) + fib(n-2)"	
if n < 2:	
return n	
fib = cache.get_key(n)	
if fib is None:	
fib = fibonacci(n - 1) + fibonacci(n - 2)	
cache.set_key(n, fib)	
return fib	
	
>>> start = time.time()	
>>> fibonacci(35)	
9227465	
>>> print("Elapsed:", time.time() - start)	
Elapsed: 0.0007810592651367188	
	
>>> start = time.time()	
>>> fibonacci(100)	
354224848179261915075	
>>> print("Elapsed:", time.time() - start)	
Elapsed: 0.0013179779052734375	

DRY:  Don’t  Repeat  Yourself!	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
Pay  a4ention  to  the  magic  trick	

{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
import time	
import cache	
	
def fibonacci(n): # The function remains unchanged	
if n < 2:	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	
	
>>> real_fibonacci = fibonacci	
	
def fibonacci(n):	
fib = cache.get_key(n)	
if fib is None:	
fib = real_fibonacci(n)	
cache.set_key(n, fib)	
return fib	
	
	
>>> start = time.time()	
>>> fibonacci(35)	
9227465	
>>> print("Elapsed:", time.time() - start)	
Elapsed: 0.0010080337524414062	

Original  function  is  not  modified	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
import time	
import cache	
	
def fibonacci(n): # The function remains unchanged	
if n < 2:	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	
	
>>> real_fibonacci = fibonacci	
	
def fibonacci(n):	
fib = cache.get_key(n)	
if fib is None:	
fib = real_fibonacci(n)	
cache.set_key(n, fib)	
return fib	
	
	
>>> start = time.time()	
>>> fibonacci(35)	
9227465	
>>> print("Elapsed:", time.time() - start)	
Elapsed: 0.0010080337524414062	

Which  function  is  called  here?	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
import time	
The  next-­‐‑to-­‐‑last  scope:	
import cache	
current  module’s  global  names	
	
def fibonacci(n): # The function remains unchanged	
if n < 2:	
The  innermost  scope:	
return n	
current  local  names	
return fibonacci(n - 1) + fibonacci(n - 2)	
	
>>> real_fibonacci = fibonacci	
	
def fibonacci(n):	
fib = cache.get_key(n)	
if fib is None:	
fib = real_fibonacci(n)	
cache.set_key(n, fib)	
return fib	
	
	
>>> start = time.time()	
>>> fibonacci(35)	
9227465	
>>> print("Elapsed:", time.time() - start)	
Elapsed: 0.0010080337524414062	

Remember  the  scopes…	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
And  now  the  trick  in  slow  motion	

{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
def fibonacci(n):	
if n < 2:	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	
	
>>> print(id(fibonacci))	
4298858568	

{	
        fibonacci:  4298858568	
}	

Global  names	

4298858568:  <function  fibonacci  at  0x1003b6048>	

if n < 2:	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	

	

Objects	

1.  Create  original  fibonacci  function	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
def fibonacci(n):	
if n < 2:	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	
	
>>> print(id(fibonacci))	
4298858568	
	
>>> real_fib = fibonacci	

{	
        fibonacci:  4298858568,	
        real_fib:        4298858568,	
}	

Global  names	

4298858568:  <function  fibonacci  at  0x1003b6048>	

if n < 2:	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	

	
	

Objects	

2.  Create  alternative  name  pointing  
to  the  same  function  object	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
def fibonacci(n):	
{	
if n < 2:	
        fibonacci:  4302081696,	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	         real_fib:        4298858568,	
	
}	
>>> print(id(fibonacci))	
4298858568	
Global  names	
	
>>> real_fib = fibonacci	
	
4298858568:  <function  fibonacci  at  0x1003b6048>	
def fibonacci(n):	
if n < 2:	
fib = cache.get_key(n)	
return n	
if fib is None:	
return fibonacci(n - 1) + fibonacci(n - 2)	
fib = real_fib (n)	
	
cache.set_key(n, fib)	
	
return fib	
4302081696:  <function  fibonacci  at  0x1006c8ea0>	
	
fib = cache.get_key(n)	
>>> print(id(fibonacci))	
if fib is None:	
4302081696	
fib = real_fib (n)	
	
cache.set_key(n, fib)	
>>> print(id(real_fib))	
return fib	
Objects	
4298858568	

	

3.  Replace  original  name  with  new  a  
function  which  calls  the  alternative  name	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
def fibonacci(n):	
{	
if n < 2:	
        fibonacci:  4302081696,	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	         real_fib:        4298858568,	
	
}	
>>> print(id(fibonacci))	
4298858568	
Global  names	
	
>>> real_fib = fibonacci	
	
4298858568:  <function  fibonacci  at  0x1003b6048>	
def fibonacci(n):	
if n < 2:	
fib = cache.get_key(n)	
return n	
if fib is None:	
return fibonacci(n - 1) + fibonacci(n - 2)	
fib = real_fib (n)	
	
cache.set_key(n, fib)	
	
return fib	
4302081696:  <function  fibonacci  at  0x1006c8ea0>	
	
fib = cache.get_key(n)	
>>> print(id(fibonacci))	
if fib is None:	
4302081696	
fib = real_fib (n)	
	
cache.set_key(n, fib)	
>>> print(id(real_fib))	
return fib	
Objects	
4298858568	

	

This  way  we  swap  both  functions	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
def fibonacci(n):	
{	
if n < 2:	
        fibonacci:  4302081696,	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	         real_fib:        4298858568,	
	
}	
>>> print(id(fibonacci))	
4298858568	
Global  names	
	
>>> real_fib = fibonacci	
	
4298858568:  <function  fibonacci  at  0x1003b6048>	
def fibonacci(n):	
if n < 2:	
fib = cache.get_key(n)	
return n	
if fib is None:	
return fibonacci(n - 1) + fibonacci(n - 2)	
fib = real_fib (n)	
	
cache.set_key(n, fib)	
	
return fib	
4302081696:  <function  fibonacci  at  0x1006c8ea0>	
	
fib = cache.get_key(n)	
>>> print(id(fibonacci))	
if fib is None:	
4302081696	
fib = real_fib (n)	
	
cache.set_key(n, fib)	
>>> print(id(real_fib))	
return fib	
Objects	
4298858568	

	

But  the  original  function  does  not  know  it	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
Let’s  make  this  trick  fully  reusable	

{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
Let’s  make  it  work  with  any*  function	

{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
import cache	
	
def memoize_any_function(func_to_memoize):	
"Return a wrapped version of the function using memoization"	
	
def memoized_version_of_func(n):	
"Wrapper using memoization"	
res = cache.get_key(n)	
if res is None:	
res = func_to_memoize(n) # Call the real function	
cache.set_key(n, res)	
return res	
return memoized_version_of_func	
	
def fibonacci(n):	
if n < 2:	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	
	
>>> fibonacci = memoize_any_function(fibonacci)	

A  factory  of  memoization  functions	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
import cache	
	
def memoize_any_function(func_to_memoize):	
"Return a wrapped version of the function using memoization"	
	
def memoized_version_of_func(n):	
"Wrapper using memoization"	
res = cache.get_key(n)	
if res is None:	
res = func_to_memoize(n) # Call the real function	
cache.set_key(n, res)	
return res	
return memoized_version_of_func	
	
def factorial(n):	
if n < 2:	
return 1	
return n * factorial(n - 1)	
	
>>> factorial= memoize_any_function(factorial)	

A  factory  of  memoization  functions	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
import time	
	
>>> start = time.time()	
>>> fibonacci(250)	
7896325826131730509282738943634332893686268675876375	
>>> print("Elapsed:", time.time() - start)	
Elapsed: 0.0009610652923583984	
	
>>> start = time.time()	
>>> factorial(250)	
3232856260909107732320814552024368470994843717673780666747942427
1128237475551112094888179153710281994509285073531894329267309317
1280899082279103027907128192167652724018926473321804118626100683
2925365133678939089569935713530175040513178760077247933065402339
0061648255522488194365725860573992226412548329822048491377217766
5064127685880715312897877767295191399084437747870258917297325515
0283241787320658188482062478582659808848825548800000000000000000
000000000000000000000000000000000000000000000	
>>> print("Elapsed:", time.time() - start)	
Elapsed: 0.00249481201171875	

It  works  with  any*  function	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
And  finally,  at  long  last,  decorators	

{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
import cache	
	
def memoize_any_function(func_to_memoize):	
"Return a wrapped version of the function using memoization"	
	
def memoized_version_of_func(n):	
"Wrapper using memoization"	
res = cache.get_key(n)	
if res is None:	
res = func_to_memoize(n) # Call the real function	
cache.set_key(n, res)	
return res	
return memoized_version_of_func	
	
	
def fibonacci(n):	
if n < 2:	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	
	
>>> fibonacci = memoize_any_function(fibonacci)	

Pay  a4ention…	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
import cache	
	
def memoize_any_function(func_to_memoize):	
"Return a wrapped version of the function using memoization"	
	
def memoized_version_of_func(n):	
"Wrapper using memoization"	
res = cache.get_key(n)	
if res is None:	
res = func_to_memoize(n) # Call the real function	
cache.set_key(n, res)	
return res	
return memoized_version_of_func	
	
@memoize_any_function	
def fibonacci(n):	
if n < 2:	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	
	
	

Did  you  spot  the  difference?	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
def fibonacci(n):	
if n < 2:	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	
	
>>> fibonacci = memoize_any_function(fibonacci)	

This  is  the  only  thing  the  @  does	
	

Calls  a  decorator  providing  the  decorated  function,  
then  makes  the  function  name  point  to  the  result	
@memoize_any_function	
def fibonacci(n):	
if n < 2:	
return n	
return fibonacci(n - 1) + fibonacci(n – 2)	

Decorators  demystified	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
def fibonacci(n):	
if n < 2:	
return n	
return fibonacci(n - 1) + fibonacci(n - 2)	
	
>>> fibonacci = memoize_any_function(fibonacci)	

This  is  the  only  thing  the  @  does	
	

Calls  a  decorator  providing  the  decorated  function,  
then  makes  the  function  name  point  to  the  result	
@memoize_any_function	
def fibonacci(n):	
if n < 2:	
return n	
return fibonacci(n - 1) + fibonacci(n – 2)	

Decorators  demystified	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
Thanks  for  coming!	
	
	

Slides:  h4p://goo.gl/bIlG9R  	

Q&A	
{  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}

More Related Content

What's hot

10 tips for making Bash a sane programming language
10 tips for making Bash a sane programming language10 tips for making Bash a sane programming language
10 tips for making Bash a sane programming languageYaroslav Tkachenko
 
The Ring programming language version 1.6 book - Part 48 of 189
The Ring programming language version 1.6 book - Part 48 of 189The Ring programming language version 1.6 book - Part 48 of 189
The Ring programming language version 1.6 book - Part 48 of 189Mahmoud Samir Fayed
 
Server Side Events
Server Side EventsServer Side Events
Server Side Eventsthepilif
 
JSON and Swift, Still A Better Love Story Than Twilight
JSON and Swift, Still A Better Love Story Than TwilightJSON and Swift, Still A Better Love Story Than Twilight
JSON and Swift, Still A Better Love Story Than TwilightDonny Wals
 
My app is secure... I think
My app is secure... I thinkMy app is secure... I think
My app is secure... I thinkWim Godden
 
Go for the would be network programmer
Go for the would be network programmerGo for the would be network programmer
Go for the would be network programmerEleanor McHugh
 
2020 Droid Knights CustomLint 적용기
2020 Droid Knights CustomLint 적용기2020 Droid Knights CustomLint 적용기
2020 Droid Knights CustomLint 적용기Insung Hwang
 
PuppetDB, Puppet Explorer and puppetdbquery
PuppetDB, Puppet Explorer and puppetdbqueryPuppetDB, Puppet Explorer and puppetdbquery
PuppetDB, Puppet Explorer and puppetdbqueryPuppet
 
solving little problems
solving little problemssolving little problems
solving little problemsAustin Ziegler
 
#SPUG - Legacy applications
#SPUG - Legacy applications#SPUG - Legacy applications
#SPUG - Legacy applicationsPiotr Pasich
 
My app is secure... I think
My app is secure... I thinkMy app is secure... I think
My app is secure... I thinkWim Godden
 
DEF CON 27 - PATRICK WARDLE - harnessing weapons of Mac destruction
DEF CON 27 - PATRICK WARDLE - harnessing weapons of Mac destructionDEF CON 27 - PATRICK WARDLE - harnessing weapons of Mac destruction
DEF CON 27 - PATRICK WARDLE - harnessing weapons of Mac destructionFelipe Prado
 
Corinna Status 2022.pptx
Corinna Status 2022.pptxCorinna Status 2022.pptx
Corinna Status 2022.pptxCurtis Poe
 
Gabriele Lana - The Magic of Elixir
Gabriele Lana - The Magic of ElixirGabriele Lana - The Magic of Elixir
Gabriele Lana - The Magic of ElixirCodemotion
 
The state of your own hypertext preprocessor
The state of your own hypertext preprocessorThe state of your own hypertext preprocessor
The state of your own hypertext preprocessorAlessandro Nadalin
 

What's hot (20)

Whispered secrets
Whispered secretsWhispered secrets
Whispered secrets
 
10 tips for making Bash a sane programming language
10 tips for making Bash a sane programming language10 tips for making Bash a sane programming language
10 tips for making Bash a sane programming language
 
The Ring programming language version 1.6 book - Part 48 of 189
The Ring programming language version 1.6 book - Part 48 of 189The Ring programming language version 1.6 book - Part 48 of 189
The Ring programming language version 1.6 book - Part 48 of 189
 
Server Side Events
Server Side EventsServer Side Events
Server Side Events
 
Finding Clojure
Finding ClojureFinding Clojure
Finding Clojure
 
JSON and Swift, Still A Better Love Story Than Twilight
JSON and Swift, Still A Better Love Story Than TwilightJSON and Swift, Still A Better Love Story Than Twilight
JSON and Swift, Still A Better Love Story Than Twilight
 
My app is secure... I think
My app is secure... I thinkMy app is secure... I think
My app is secure... I think
 
Go for the would be network programmer
Go for the would be network programmerGo for the would be network programmer
Go for the would be network programmer
 
Php introduction
Php introductionPhp introduction
Php introduction
 
2020 Droid Knights CustomLint 적용기
2020 Droid Knights CustomLint 적용기2020 Droid Knights CustomLint 적용기
2020 Droid Knights CustomLint 적용기
 
PuppetDB, Puppet Explorer and puppetdbquery
PuppetDB, Puppet Explorer and puppetdbqueryPuppetDB, Puppet Explorer and puppetdbquery
PuppetDB, Puppet Explorer and puppetdbquery
 
solving little problems
solving little problemssolving little problems
solving little problems
 
Don't do this
Don't do thisDon't do this
Don't do this
 
#SPUG - Legacy applications
#SPUG - Legacy applications#SPUG - Legacy applications
#SPUG - Legacy applications
 
Kamailio and VoIP Wild World
Kamailio and VoIP Wild WorldKamailio and VoIP Wild World
Kamailio and VoIP Wild World
 
My app is secure... I think
My app is secure... I thinkMy app is secure... I think
My app is secure... I think
 
DEF CON 27 - PATRICK WARDLE - harnessing weapons of Mac destruction
DEF CON 27 - PATRICK WARDLE - harnessing weapons of Mac destructionDEF CON 27 - PATRICK WARDLE - harnessing weapons of Mac destruction
DEF CON 27 - PATRICK WARDLE - harnessing weapons of Mac destruction
 
Corinna Status 2022.pptx
Corinna Status 2022.pptxCorinna Status 2022.pptx
Corinna Status 2022.pptx
 
Gabriele Lana - The Magic of Elixir
Gabriele Lana - The Magic of ElixirGabriele Lana - The Magic of Elixir
Gabriele Lana - The Magic of Elixir
 
The state of your own hypertext preprocessor
The state of your own hypertext preprocessorThe state of your own hypertext preprocessor
The state of your own hypertext preprocessor
 

Similar to Decorators demystified

The (unknown) collections module
The (unknown) collections moduleThe (unknown) collections module
The (unknown) collections modulePablo Enfedaque
 
Python: the coolest is yet to come
Python: the coolest is yet to comePython: the coolest is yet to come
Python: the coolest is yet to comePablo Enfedaque
 
Execution model and other must-know's
Execution model and other must-know'sExecution model and other must-know's
Execution model and other must-know'sPablo Enfedaque
 
Effective Java with Groovy - How Language Influences Adoption of Good Practices
Effective Java with Groovy - How Language Influences Adoption of Good PracticesEffective Java with Groovy - How Language Influences Adoption of Good Practices
Effective Java with Groovy - How Language Influences Adoption of Good PracticesNaresha K
 
Python Functions Tutorial | Working With Functions In Python | Python Trainin...
Python Functions Tutorial | Working With Functions In Python | Python Trainin...Python Functions Tutorial | Working With Functions In Python | Python Trainin...
Python Functions Tutorial | Working With Functions In Python | Python Trainin...Edureka!
 
Isolated development in python
Isolated development in pythonIsolated development in python
Isolated development in pythonAndrés J. Díaz
 
FrenchKit 2017: Server(less) Swift
FrenchKit 2017: Server(less) SwiftFrenchKit 2017: Server(less) Swift
FrenchKit 2017: Server(less) SwiftChris Bailey
 
Terrastore - A document database for developers
Terrastore - A document database for developersTerrastore - A document database for developers
Terrastore - A document database for developersSergio Bossa
 
Python Code Camp for Professionals 4/4
Python Code Camp for Professionals 4/4Python Code Camp for Professionals 4/4
Python Code Camp for Professionals 4/4DEVCON
 
Component-Oriented Web Development with Dart
Component-Oriented Web Development with DartComponent-Oriented Web Development with Dart
Component-Oriented Web Development with DartC4Media
 
OpenWhisk: Event-driven Design
OpenWhisk: Event-driven DesignOpenWhisk: Event-driven Design
OpenWhisk: Event-driven DesignAltoros
 
Herding types with Scala macros
Herding types with Scala macrosHerding types with Scala macros
Herding types with Scala macrosMarina Sigaeva
 
Building a Portfolio With Custom Post Types
Building a Portfolio With Custom Post TypesBuilding a Portfolio With Custom Post Types
Building a Portfolio With Custom Post TypesAlex Blackie
 
Improving code quality with loose coupling
Improving code quality with loose couplingImproving code quality with loose coupling
Improving code quality with loose couplingNemanja Ljubinković
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksMongoDB
 
The Ring programming language version 1.5.2 book - Part 39 of 181
The Ring programming language version 1.5.2 book - Part 39 of 181The Ring programming language version 1.5.2 book - Part 39 of 181
The Ring programming language version 1.5.2 book - Part 39 of 181Mahmoud Samir Fayed
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfShaiAlmog1
 

Similar to Decorators demystified (20)

The (unknown) collections module
The (unknown) collections moduleThe (unknown) collections module
The (unknown) collections module
 
Python: the coolest is yet to come
Python: the coolest is yet to comePython: the coolest is yet to come
Python: the coolest is yet to come
 
Execution model and other must-know's
Execution model and other must-know'sExecution model and other must-know's
Execution model and other must-know's
 
Effective Java with Groovy - How Language Influences Adoption of Good Practices
Effective Java with Groovy - How Language Influences Adoption of Good PracticesEffective Java with Groovy - How Language Influences Adoption of Good Practices
Effective Java with Groovy - How Language Influences Adoption of Good Practices
 
Python Functions Tutorial | Working With Functions In Python | Python Trainin...
Python Functions Tutorial | Working With Functions In Python | Python Trainin...Python Functions Tutorial | Working With Functions In Python | Python Trainin...
Python Functions Tutorial | Working With Functions In Python | Python Trainin...
 
Sbt for mere mortals
Sbt for mere mortalsSbt for mere mortals
Sbt for mere mortals
 
Isolated development in python
Isolated development in pythonIsolated development in python
Isolated development in python
 
FrenchKit 2017: Server(less) Swift
FrenchKit 2017: Server(less) SwiftFrenchKit 2017: Server(less) Swift
FrenchKit 2017: Server(less) Swift
 
Terrastore - A document database for developers
Terrastore - A document database for developersTerrastore - A document database for developers
Terrastore - A document database for developers
 
Python Code Camp for Professionals 4/4
Python Code Camp for Professionals 4/4Python Code Camp for Professionals 4/4
Python Code Camp for Professionals 4/4
 
Component-Oriented Web Development with Dart
Component-Oriented Web Development with DartComponent-Oriented Web Development with Dart
Component-Oriented Web Development with Dart
 
OpenWhisk: Event-driven Design
OpenWhisk: Event-driven DesignOpenWhisk: Event-driven Design
OpenWhisk: Event-driven Design
 
Python1
Python1Python1
Python1
 
Herding types with Scala macros
Herding types with Scala macrosHerding types with Scala macros
Herding types with Scala macros
 
Building a Portfolio With Custom Post Types
Building a Portfolio With Custom Post TypesBuilding a Portfolio With Custom Post Types
Building a Portfolio With Custom Post Types
 
Improving code quality with loose coupling
Improving code quality with loose couplingImproving code quality with loose coupling
Improving code quality with loose coupling
 
Ruby
RubyRuby
Ruby
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
 
The Ring programming language version 1.5.2 book - Part 39 of 181
The Ring programming language version 1.5.2 book - Part 39 of 181The Ring programming language version 1.5.2 book - Part 39 of 181
The Ring programming language version 1.5.2 book - Part 39 of 181
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdf
 

Recently uploaded

GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphNeo4j
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...HostedbyConfluent
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptxLBM Solutions
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksSoftradix Technologies
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?XfilesPro
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 

Recently uploaded (20)

GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptx
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other Frameworks
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 

Decorators demystified

  • 1. Python  DECORATORS   DEMYSTIFIED { “event”:      “PyCon  ES  2013” “author”:  “Pablo  Enfedaque” “twi4er”:  “pablitoev56”
  • 2. Do  you  know  what’s  happening  each   time  you  use  the  @    (at)  symbol  to   decorate  a  function  or  class? Today  we  are  going  to  see  how   Python’s  decorators  syntactic  sugar   works  under  the  hood Welcome! {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 3. And  that’s  why  we  will  talk  about   Python  namespaces  and  scopes Welcome! {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 4. And  finally  will  manually  implement   and  apply  a  handcrafted  decorator Welcome! {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 5. Let’s  start  implementing  some useful  stuff  for  the  talk {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 6. from collections import OrderedDict CACHE = OrderedDict() MAX_SIZE = 100 def set_key(key, value): "Set a key value, removing oldest key if MAX_SIZE exceeded" CACHE[key] = value if len(CACHE) > MAX_SIZE: CACHE.popitem(last=False) def get_key(key): "Retrieve a key value from the cache, or None if not found" return CACHE.get(key, None) >>> set_key("my_key", "the_value”) >>> print(get_key("my_key")) the_value >>> print(get_key("not_found_key")) None A  simple  software  cache {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 7. def factorial(n): "Return n! (the factorial of n): n! = n * (n-1)!" if n < 2: return 1 return n * factorial(n - 1) >>> list(map(factorial, range(10))) [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880] def fibonacci(n): "Return nth fibonacci number: fib(n) = fib(n-1) + fib(n-2)" if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) >>> list(map(fibonacci, range(10))) [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] Factorial  and  fibonacci  functions {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 8. Pre4y  easy,  right? {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 9. However… {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 10. from collections import OrderedDict CACHE = OrderedDict() MAX_SIZE = 100 def set_key(key, value): "Set a key value, removing oldest key if MAX_SIZE exceeded" CACHE[key] = value if len(CACHE) > MAX_SIZE: CACHE.popitem(last=False) def get_key(key): "Retrieve a key value from the cache, or None if not found" return CACHE.get(key, None) >>> set_key("my_key", "the_value”) >>> print(get_key("my_key")) the_value >>> print(get_key("not_found_key")) None How  do  we  access  this  a4ribute? {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 11. def factorial(n): "Return n! (the factorial of n): n! = n * (n-1)!" if n < 2: return 1 return n * factorial(n - 1) >>> list(map(factorial, range(10))) [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880] def fibonacci(n): "Return nth fibonacci number: fib(n) = fib(n-1) + fib(n-2)" if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) >>> list(map(fibonacci, range(10))) [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] How  are  recursive  calls  possible? {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 12. >>> from os import path >>> print(type(path), id(path)) <class 'module'> 4300435112 >>> from sys import path >>> print(type(path), id(path)) <class 'list'> 4298480008 def split_path(path, sep="/"): print(type(path), id(path)) return path.split(sep) >>> split_path("/this/is/a/full/path") <class 'str'> 4302038120 ['', 'this', 'is', 'a', 'full', 'path'] A  simpler  case {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 13. >>> from os import path >>> print(type(path), id(path)) <class 'module'> 4300435112 >>> from sys import path >>> print(type(path), id(path)) <class 'list'> 4298480008 def split_path(path, sep="/"): print(type(path), id(path)) return path.split(sep) >>> split_path("/this/is/a/full/path") <class 'str'> 4302038120 ['', 'this', 'is', 'a', 'full', 'path'] The  same  name  defined  several  times? {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 14. Let  me  introduce  Python  namespaces {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 15. A  namespace  is  a  mapping   from  names  to  objects {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 16. >  The  set  of  built-­‐‑in  names  (functions,  exceptions) >  Global  names  in  a  module  (including  imports) >  Local  names  in  a  function  invocation >  Names  defined  in  top-­‐‑level  invocation  of  interpreter Python  namespaces  examples {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 17. There  is  no  relation  between  names  in   different  namespaces Two  modules  or  functions  may  define  the  same   name  without  confusion Python  namespaces {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 18. Namespaces  are  created  (and  deleted)   at  different  moments  and  have   different  lifetimes Python  namespaces {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 19. The  built-­‐‑ins  namespace  is  created   when  the  Python  interpreter  starts And  is  never  deleted Python  namespaces  lifetimes {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 20. A  module  global  namespace  is   created  when  the  module  definition  is   read-­‐‑in  (when  it  is  imported) Normally  it  lasts  until  the  interpreter  quits Python  namespaces  lifetimes {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 21. A  function  local  namespace  is  created   each  time  it  is  called It  is  deleted  when  the  function  returns  or  raises Python  namespaces  lifetimes {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 22. And  what  about  Python  scopes? {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 23. A  scope  is  a  textual  region   of  a  program  where  a   namespace  is  directly   accessible {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 24. Scopes  are  determined  statically but  used  dynamically Python  scopes {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 25. At  any  time  during  execution,  there   are  at  least  three  nested  scopes  whose   namespaces  are  directly  accessible Python  scopes {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 26. 1.  The  innermost  scope  contains  the  local   names >  2.  3.  The  scopes  of  any  enclosing  functions,   with  non-­‐‑local,  but  also  non-­‐‑global  names The  next-­‐‑to-­‐‑last  scope  contains  the   current  module'ʹs  global  names The  outermost  scope  is  the  namespace   containing  built-­‐‑in  names Python  nested  scopes {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 27. Names  are  searched  in  nested  scopes   from  inside  out From  locals  to  built-­‐‑ins Python  nested  scopes {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 28. $ python Python 2.7.5 (default, Aug 25 2013, 00:04:04) [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import cache >>> cache.set_key("my_key", 7) >>> cache.get_key("my_key") 7 >>> """ Simple cache implementation """ from collections import OrderedDict CACHE = OrderedDict() MAX_SIZE = 100 def set_key(key, value): "Set a key value, removing oldest key if MAX_SIZE exceeded" CACHE[key] = value if len(CACHE) > MAX_SIZE: CACHE.popitem(last=False) def get_key(key): "Retrieve a key value from the cache, or None if not found" return CACHE.get(key, None) Python  scopes {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 29. $ python Python 2.7.5 (default, Aug 25 2013, 00:04:04) [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import cache >>> cache.set_key("my_key", 7) >>> cache.get_key("my_key") 7 >>> """ The  outermost  scope: built-­‐‑in  names The  next-­‐‑to-­‐‑last  scope: Simple cache implementation current  module’s  global  names """ from collections import OrderedDict CACHE = OrderedDict() MAX_SIZE = 100 def set_key(key, value): "Set a key value, removing oldest key if MAX_SIZE exceeded" CACHE[key] = value if len(CACHE) > MAX_SIZE: The  innermost  scope: CACHE.popitem(last=False) current  local  names def get_key(key): "Retrieve a key value from the cache, or None if not found" return CACHE.get(key, None) Python  scopes {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 30. def get_power_func(y): print("Creating function to raise to {}".format(y)) def power_func(x): print("Calling to raise {} to power of {}".format(x, y)) x = pow(x, y) return x return power_func >>> raise_to_4 = get_power_func(4) Creating function to raise to 3 >>> x = 3 >>> print(raise_to_4(x)) Calling to raise 3 to power of 4 81 >>> print(x) 3 Another  more  complex  case {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 31. def get_power_func(y): print("Creating function to raise to {}".format(y)) def power_func(x): print("Calling to raise {} to power of {}".format(x, y)) x = pow(x, y) return x return power_func >>> raise_to_4 = get_power_func(4) Creating function to raise to 3 >>> x = 3 >>> print(raise_to_4(x)) Calling to raise 3 to power of 4 81 >>> print(x) 3 Where  is  y  defined? {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 32. def get_power_func(y): print("Creating function to raise to {}".format(y)) def power_func(x): print("Calling to raise {} to power of {}".format(x, y)) x = pow(x, y) return x The  innermost  scope:  local  names return power_func Enclosing  function  scope: >>> raise_to_4 = get_power_func(4) non-­‐‑local  non-­‐‑global  names Creating function to raise to 3 >>> x = 3 >>> print(raise_to_4(x)) Calling to raise 3 to power of 4 81 The  next-­‐‑to-­‐‑last  scope: >>> print(x) 3 current  module’s  global  names Nested  scopes {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 33. def get_power_func(y): print("Creating function to raise to {}".format(y)) def power_func(x): print("Calling to raise {} to power of {}".format(x, y)) x = pow(x, y) return x return power_func >>> raise_to_4 = get_power_func(4) Creating function to raise to 3 >>> print(raise_to_4.__globals__) {'x': 3, 'raise_to_4': <function get_power_func.<locals>.power_func at 0x100658488>, 'get_power_func': <function get_power_func at 0x1003b6048>, ...} >>> print(raise_to_4.__closure__) (<cell at 0x10065f048: int object at 0x10023b280>,) There  is  a  closure! {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 34. A  function  closure  is  a   reference  to  each  of  the  non-­‐‑ local  variables  of  the  function {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 35. def get_power_func(y): print("Creating function to raise to {}".format(y)) def power_func(x): print("Calling to raise {} to power of {}".format(x, y)) x = pow(x, y) return x return power_func >>> raise_to_4 = get_power_func(4) Creating function to raise to 3 >>> print(raise_to_4.__globals__) {'x': 3, 'raise_to_4': <function get_power_func.<locals>.power_func at 0x100658488>, 'get_power_func': <function get_power_func at 0x1003b6048>, ...} >>> print(raise_to_4.__closure__) (<cell at 0x10065f048: int object at 0x10023b280>,) So,  where  is  y  defined? {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 36. Well,  maybe  you  are  wondering where  are  the  decorators  in  this  talk… {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 37. {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 38. So,  let’s  manually  apply  a  decorator {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 39. def factorial(n): "Return n! (the factorial of n): n! = n * (n-1)!" if n < 2: return 1 return n * factorial(n - 1) >>> start = time.time() >>> factorial(35) 10333147966386144929666651337523200000000 >>> print("Elapsed:", time.time() - start) Elapsed: 0.0007369518280029297 def fibonacci(n): "Return nth fibonacci number: fib(n) = fib(n-1) + fib(n-2)" if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) >>> start = time.time() >>> fibonacci(35) 9227465 >>> print("Elapsed:", time.time() - start) Elapsed: 6.916048049926758 Let’s  go  back  to  these  functions {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 40. 4 3 2 5 2 1 1 1 0 0 3 2 1 1 0 fibonacci(5)  recursive  calls  graph {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 41. from collections import OrderedDict CACHE = OrderedDict() MAX_SIZE = 100 def set_key(key, value): "Set a key value, removing oldest key if MAX_SIZE exceeded" CACHE[key] = value if len(CACHE) > MAX_SIZE: CACHE.popitem(last=False) def get_key(key): "Retrieve a key value from the cache, or None if not found" return CACHE.get(key, None) >>> set_key("my_key", "the_value”) >>> print(get_key("my_key")) the_value >>> print(get_key("not_found_key")) None Do  you  remember  we  have  a  cache? {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 42. {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 43. import cache def fibonacci(n): "Return nth fibonacci number: fib(n) = fib(n-1) + fib(n-2)" if n < 2: return n fib = cache.get_key(n) if fib is None: fib = fibonacci(n - 1) + fibonacci(n - 2) cache.set_key(n, fib) return fib >>> start = time.time() >>> fibonacci(35) 9227465 >>> print("Elapsed:", time.time() - start) Elapsed: 0.0007810592651367188 >>> start = time.time() >>> fibonacci(100) 354224848179261915075 >>> print("Elapsed:", time.time() - start) Elapsed: 0.0013179779052734375 What  about  this  version? {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 44. import cache def fibonacci(n): "Return nth fibonacci number: fib(n) = fib(n-1) + fib(n-2)" if n < 2: return n fib = cache.get_key(n) if fib is None: fib = fibonacci(n - 1) + fibonacci(n - 2) cache.set_key(n, fib) return fib >>> start = time.time() >>> fibonacci(35) 9227465 >>> print("Elapsed:", time.time() - start) Elapsed: 0.0007810592651367188 >>> start = time.time() >>> fibonacci(100) 354224848179261915075 >>> print("Elapsed:", time.time() - start) Elapsed: 0.0013179779052734375 DRY:  Don’t  Repeat  Yourself! {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 45. Pay  a4ention  to  the  magic  trick {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 46. import time import cache def fibonacci(n): # The function remains unchanged if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) >>> real_fibonacci = fibonacci def fibonacci(n): fib = cache.get_key(n) if fib is None: fib = real_fibonacci(n) cache.set_key(n, fib) return fib >>> start = time.time() >>> fibonacci(35) 9227465 >>> print("Elapsed:", time.time() - start) Elapsed: 0.0010080337524414062 Original  function  is  not  modified {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 47. import time import cache def fibonacci(n): # The function remains unchanged if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) >>> real_fibonacci = fibonacci def fibonacci(n): fib = cache.get_key(n) if fib is None: fib = real_fibonacci(n) cache.set_key(n, fib) return fib >>> start = time.time() >>> fibonacci(35) 9227465 >>> print("Elapsed:", time.time() - start) Elapsed: 0.0010080337524414062 Which  function  is  called  here? {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 48. import time The  next-­‐‑to-­‐‑last  scope: import cache current  module’s  global  names def fibonacci(n): # The function remains unchanged if n < 2: The  innermost  scope: return n current  local  names return fibonacci(n - 1) + fibonacci(n - 2) >>> real_fibonacci = fibonacci def fibonacci(n): fib = cache.get_key(n) if fib is None: fib = real_fibonacci(n) cache.set_key(n, fib) return fib >>> start = time.time() >>> fibonacci(35) 9227465 >>> print("Elapsed:", time.time() - start) Elapsed: 0.0010080337524414062 Remember  the  scopes… {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 49. And  now  the  trick  in  slow  motion {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 50. def fibonacci(n): if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) >>> print(id(fibonacci)) 4298858568 {        fibonacci:  4298858568 } Global  names 4298858568:  <function  fibonacci  at  0x1003b6048> if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) Objects 1.  Create  original  fibonacci  function {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 51. def fibonacci(n): if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) >>> print(id(fibonacci)) 4298858568 >>> real_fib = fibonacci {        fibonacci:  4298858568,        real_fib:        4298858568, } Global  names 4298858568:  <function  fibonacci  at  0x1003b6048> if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) Objects 2.  Create  alternative  name  pointing   to  the  same  function  object {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 52. def fibonacci(n): { if n < 2:        fibonacci:  4302081696, return n return fibonacci(n - 1) + fibonacci(n - 2)        real_fib:        4298858568, } >>> print(id(fibonacci)) 4298858568 Global  names >>> real_fib = fibonacci 4298858568:  <function  fibonacci  at  0x1003b6048> def fibonacci(n): if n < 2: fib = cache.get_key(n) return n if fib is None: return fibonacci(n - 1) + fibonacci(n - 2) fib = real_fib (n) cache.set_key(n, fib) return fib 4302081696:  <function  fibonacci  at  0x1006c8ea0> fib = cache.get_key(n) >>> print(id(fibonacci)) if fib is None: 4302081696 fib = real_fib (n) cache.set_key(n, fib) >>> print(id(real_fib)) return fib Objects 4298858568 3.  Replace  original  name  with  new  a   function  which  calls  the  alternative  name {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 53. def fibonacci(n): { if n < 2:        fibonacci:  4302081696, return n return fibonacci(n - 1) + fibonacci(n - 2)        real_fib:        4298858568, } >>> print(id(fibonacci)) 4298858568 Global  names >>> real_fib = fibonacci 4298858568:  <function  fibonacci  at  0x1003b6048> def fibonacci(n): if n < 2: fib = cache.get_key(n) return n if fib is None: return fibonacci(n - 1) + fibonacci(n - 2) fib = real_fib (n) cache.set_key(n, fib) return fib 4302081696:  <function  fibonacci  at  0x1006c8ea0> fib = cache.get_key(n) >>> print(id(fibonacci)) if fib is None: 4302081696 fib = real_fib (n) cache.set_key(n, fib) >>> print(id(real_fib)) return fib Objects 4298858568 This  way  we  swap  both  functions {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 54. def fibonacci(n): { if n < 2:        fibonacci:  4302081696, return n return fibonacci(n - 1) + fibonacci(n - 2)        real_fib:        4298858568, } >>> print(id(fibonacci)) 4298858568 Global  names >>> real_fib = fibonacci 4298858568:  <function  fibonacci  at  0x1003b6048> def fibonacci(n): if n < 2: fib = cache.get_key(n) return n if fib is None: return fibonacci(n - 1) + fibonacci(n - 2) fib = real_fib (n) cache.set_key(n, fib) return fib 4302081696:  <function  fibonacci  at  0x1006c8ea0> fib = cache.get_key(n) >>> print(id(fibonacci)) if fib is None: 4302081696 fib = real_fib (n) cache.set_key(n, fib) >>> print(id(real_fib)) return fib Objects 4298858568 But  the  original  function  does  not  know  it {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 55. Let’s  make  this  trick  fully  reusable {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 56. Let’s  make  it  work  with  any*  function {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 57. import cache def memoize_any_function(func_to_memoize): "Return a wrapped version of the function using memoization" def memoized_version_of_func(n): "Wrapper using memoization" res = cache.get_key(n) if res is None: res = func_to_memoize(n) # Call the real function cache.set_key(n, res) return res return memoized_version_of_func def fibonacci(n): if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) >>> fibonacci = memoize_any_function(fibonacci) A  factory  of  memoization  functions {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 58. import cache def memoize_any_function(func_to_memoize): "Return a wrapped version of the function using memoization" def memoized_version_of_func(n): "Wrapper using memoization" res = cache.get_key(n) if res is None: res = func_to_memoize(n) # Call the real function cache.set_key(n, res) return res return memoized_version_of_func def factorial(n): if n < 2: return 1 return n * factorial(n - 1) >>> factorial= memoize_any_function(factorial) A  factory  of  memoization  functions {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 59. import time >>> start = time.time() >>> fibonacci(250) 7896325826131730509282738943634332893686268675876375 >>> print("Elapsed:", time.time() - start) Elapsed: 0.0009610652923583984 >>> start = time.time() >>> factorial(250) 3232856260909107732320814552024368470994843717673780666747942427 1128237475551112094888179153710281994509285073531894329267309317 1280899082279103027907128192167652724018926473321804118626100683 2925365133678939089569935713530175040513178760077247933065402339 0061648255522488194365725860573992226412548329822048491377217766 5064127685880715312897877767295191399084437747870258917297325515 0283241787320658188482062478582659808848825548800000000000000000 000000000000000000000000000000000000000000000 >>> print("Elapsed:", time.time() - start) Elapsed: 0.00249481201171875 It  works  with  any*  function {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 60. And  finally,  at  long  last,  decorators {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 61. import cache def memoize_any_function(func_to_memoize): "Return a wrapped version of the function using memoization" def memoized_version_of_func(n): "Wrapper using memoization" res = cache.get_key(n) if res is None: res = func_to_memoize(n) # Call the real function cache.set_key(n, res) return res return memoized_version_of_func def fibonacci(n): if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) >>> fibonacci = memoize_any_function(fibonacci) Pay  a4ention… {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 62. import cache def memoize_any_function(func_to_memoize): "Return a wrapped version of the function using memoization" def memoized_version_of_func(n): "Wrapper using memoization" res = cache.get_key(n) if res is None: res = func_to_memoize(n) # Call the real function cache.set_key(n, res) return res return memoized_version_of_func @memoize_any_function def fibonacci(n): if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) Did  you  spot  the  difference? {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 63. def fibonacci(n): if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) >>> fibonacci = memoize_any_function(fibonacci) This  is  the  only  thing  the  @  does Calls  a  decorator  providing  the  decorated  function,   then  makes  the  function  name  point  to  the  result @memoize_any_function def fibonacci(n): if n < 2: return n return fibonacci(n - 1) + fibonacci(n – 2) Decorators  demystified {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 64. def fibonacci(n): if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) >>> fibonacci = memoize_any_function(fibonacci) This  is  the  only  thing  the  @  does Calls  a  decorator  providing  the  decorated  function,   then  makes  the  function  name  point  to  the  result @memoize_any_function def fibonacci(n): if n < 2: return n return fibonacci(n - 1) + fibonacci(n – 2) Decorators  demystified {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}
  • 65. Thanks  for  coming! Slides:  h4p://goo.gl/bIlG9R   Q&A {  “event”:  “PyCon  ES  2013”,  “author”:  “Pablo  Enfedaque”,  “twi4er”:  “pablitoev56”}