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'
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'
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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' |
Pickle, Decorator.
The decorated function has the same name as the original function, makes it un-pickle-able.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
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