使用搁置保存功能

时间:2016-08-24 11:05:22

标签: python python-2.7

我试图使用shelve python模块来保存我的会话输出并稍后重新加载它,但我发现如果我已经定义了函数,那么我在重新加载阶段会出错。我这样做的方式有问题吗?我的代码基于How can I save all the variables in the current python session?的答案。

这里有一些简单的代码可以重现错误:

def test_fn():  #simple test function
    return

import shelve
my_shelf = shelve.open('test_shelve','n')

for key in globals().keys():
    try:
        my_shelf[key] = globals()[key]
    except:  #__builtins__, my_shelf, and imported modules cannot be shelved.
        pass

my_shelf.close()

然后,如果我退出,我可以做

ls -lh test_shelve*
-rw-r--r-- 1 user group  22K Aug 24 11:16 test_shelve.bak
-rw-r--r-- 1 user group 476K Aug 24 11:16 test_shelve.dat
-rw-r--r-- 1 user group  22K Aug 24 11:16 test_shelve.dir

通常,在新的IPython会话中,我希望能够执行以下操作:

import shelve
my_shelf = shelve.open('test_shelve')
for key in my_shelf:
    globals()[key]=my_shelf[key]

这会导致key' test_fn'出现错误。以下是一些演示错误的代码:

print my_shelf['test_fn']
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-4-deb481380237> in <module>()
----> 1 print my_shelf['test_fn']

/home/user/anaconda2/envs/main/lib/python2.7/shelve.pyc in __getitem__(self, key)
    120         except KeyError:
    121             f = StringIO(self.dict[key])
--> 122             value = Unpickler(f).load()
    123             if self.writeback:
    124                 self.cache[key] = value

AttributeError: 'module' object has no attribute 'test_fn'

当然,一个解决方案是在保存阶段排除函数,但从我读过的内容应该可以使用这种方法恢复它们,所以我想知道我是否做错了。

2 个答案:

答案 0 :(得分:3)

您无法使用shelve(或pickleshelve使用的实际协议)来存储可执行代码,否。

存储的内容是函数的引用(只是可以再次导入函数的位置)。代码不是数据,只有您引用函数的事实才是数据。 Pickle希望能够在加载存储的信息时再次加载相同的模块并运行。

这同样适用于班级;如果你挑选一个类的引用,或者pickle一个类的实例,那么只存储再次导入该类的信息(重新创建引用或实例)。

所有这一切都已完成,因为已经具有该函数或类的持久且可加载的表示形式:定义它们的模块。无需存储另一份副本。

What can be pickled and unpickled? section中明确记录了这一点:

  

请注意,函数(内置和用户定义)由“完全限定”的名称引用而非值引用。这意味着只有函数名称被腌制,以及定义函数的模块的名称。函数的代码或其任何函数属性都不会被pickle。因此,定义模块必须可以在unpickling环境中导入,并且模块必须包含命名对象,否则将引发异常。

为您的具体示例详细介绍:Python执行的主要脚本称为__main__模块,您搁置了__main__.test_fn函数。然后存储的内容只是一个标记,表示您引用了全局和导入位置,因此存储了接近GLOBAL__main__test_fn的内容。再次加载搁置数据时,在看到GLOBAL标记后,pickle模块会尝试从test_fn模块加载名称__main__。由于您的第二个脚本再次作为__main__加载,但没有test_fn全局,因此加载引用失败。

答案 1 :(得分:0)

您不能在架子中存储函数,但是可以存储字符串,因此将函数存储为字符串,然后使用exec()或eval()从架子上使用它们 要将函数更改为字符串,请使用inpect.getsource() 但是,直接运行解释器时不能使用inspect.getsource

存储函数时

#function
sq=lambda x:x*x
def sum(n,n2):
    return n+n2

#---------------
import shelve
from inspect import getsource
with shelve.open('path to shelf') as mem:
    sf=getsource(sum)
    mem['sum function']='def {}'+sf[sf.index('('):]
    mem['lambda function']=getsource(sq)
如果没有运行文件,

getsource()将不起作用

将函数放入程序时:

import shelve
with shelve.open('path to shelf') as mem:
    previous_lambda=eval(mem['lambda function'])
    exec(mem['sum function'].format('prev_f'))#prev_f being the name of the function


#now you can use your functions
print(previous_lambda(4))
print(prev_f(2,3))

如果您需要知道一个函数是lambda还是普通函数(因为它们的存储方式不同):

if func.__name__=="<lambda>":
    print('func is lambda')
elif callable(func):
    print('func is an ordinary function')
else:
    print('func is not a function')