Wednesday, October 29, 2014

Decoration, Pickle, Closure.

What is closure? What it has to do with pickle-able ? 

A closure occurs when a function has access to a local variable from an enclosing scope that has finished its execution.
Here is an example, test2 doesn't work because pickle works with name. test3 doesn't work because it conflicts with top-level function 'foo'

from functools import wraps
import pickle
import sys
def foo(x):
def bar():
return x**2
return bar
if __name__ == '__main__':
try:
c = int(raw_input('input 1,2,3 to choose which test to run: '))
except ValueError:
print 'not a number'
# this will work, because pickle needs a name
if c == 1:
print 'test1'
bar = foo(2)
with open('./delme', 'w') as ff:
pickle.dump(bar, ff)
elif c == 2:
print 'test2'
# this will not work, because pickle needs a name
with open('./delme', 'w') as ff:
pickle.dump(foo(2), ff)
elif c == 3:
print 'test3'
# this will not work, because of the name conflict
foo = foo(2)
with open('./delme', 'w') as ff:
pickle.dump(foo, ff)
else:
print 'input out of range'
view raw closure_2.py hosted with ❤ by GitHub


Pickle, Decorator. 

The decorated function has the same name as the original function, makes it un-pickle-able.

from functools import wraps
import pickle
import sys
def logged(func):
"""
if not wrapped, the decoreated function has the same name as the original
cannot pickle
"""
# @wraps(func)
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging
def f(x):
"""does some math
"""
return x**2
if __name__ == '__main__':
try:
c = int(raw_input("input 1,2,3: "))
except ValueError:
print 'not an int'
if c == 1:
"""
this doesn't work because the name conflict
"""
print 'test1'
"""
note this is equivalent to decoration syntax:
@logged
def f(x) ....
"""
f = logged(f)
print f(2)
# name is with_logging, lose of function information
print f.__name__
with open('./delme', 'w') as ff:
pickle.dump(f, ff)
elif c == 2:
"""
this works, the returned fucntion name has to match __name__
which is 'with_logging' in our case, this is a huge limitation.
for example, in debugging
"""
print 'test2'
with_logging = logged(f)
print with_logging(2)
# name is with_logging, lose of function information
print with_logging.__name__
with open('./delme', 'w') as ff:
pickle.dump(with_logging, ff)
elif c == 3:
"""
wraps() is also a decorator that updates the function information
such as names ....
it takes a function as a parameter
using wraps() is equivalent to
@wraps(func)
def with_logging( ...
"""
# this works
print 'test3'
f = wraps(f)(logged(f))
print f(2)
# no lose of function information
print f.__name__
with open('./delme', 'w') as ff:
pickle.dump(f, ff)
view raw wraps.py hosted with ❤ by GitHub
the right way to write a decorator function is to always use 'wraps'. Be able to pickled is crucial when you programming multiprocess.


No comments:

Post a Comment