我正在尝试将Flask与Dill集成在服务器端转储/加载Python会话。下面的代码有两个函数,第一个函数将x
的值设置为零并导入datetime
库。第二个将x
加1并获得时间戳。
第一个函数转储会话,第二个函数加载会话。
在转储中,pickle文件是正确生成的,但是我不能重用x
或获取时间戳。
这是当我尝试在第二个函数中执行x = x + 1
时的错误:
UnboundLocalError: local variable 'x' referenced before assignment
Dill可以与Flask一起使用吗?我需要一个不同的方法吗?
代码:
from flask import Flask
from dill import dump_session, load_session
app = Flask(__name__)
app.config['SECRET_KEY'] = 'super secret'
session_file = '/tmp/session.pkl'
@app.route('/start_counter')
def start_counter():
import datetime
x = 0
dump_session(filename = session_file)
return 'New counter started!'
@app.route('/count')
def count():
load_session(filename = session_file)
x = x + 1
now = datetime.datetime.now()
dump_session(filename = session_file)
return str(x) + '-' + str(now)
如何修复?
为了简单起见,您需要一个数据结构来保存应用程序状态。我会使用dict
,因为它很简单,但你也可以为它定义一个类。
最简单(也是最繁琐)的方法是每次需要应用程序状态时调用state = dill.load('filename')
和dill.dump(object,'filename')
。
如果您的应用程序很小,那么将起作用。如果您需要维护一个适当的应用程序状态,您应该使用数据库。
Ok。但这里发生了什么?
dill和Flask没有兼容性问题。
调用dill.dump_session()
时,保存__main__
的状态。
但是,当你在函数count()
中增加x
时,它是未定义的,因为它没有被dill保存。
一个简单的方法是将breakpoint()
放在x = x + 1
之前,或者将内容打印在try..except
子句中:
try:
print(x)
except ee:
print(ee)
pass;
x = x + 1
所以,它没有工作,因为变量x
没有在__main__
中定义,而是在start_counter()
函数的范围内,dill.load_session()
恢复了__main__
中的东西。
__main__
是什么意思?
让我们看看使用Repl:
~/$ python
Python 3.8.10 (tags/v3.8.10:3d8993a, May 3 2021, 11:48:03) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
完美。我们有一个空的python解释器。dir()
显示了我们在__main__
中的内容。
现在我们将加载一些库,赋值一个变量,定义一个函数,因为我们可以:
>>> import pandas, numpy, dill, pickle, json, datetime
>>> foo = "bar"
>>> def functionWithUglyName():
... print("yep")
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'datetime', 'dill', 'foo', 'functionWithUglyName', 'json', 'numpy', 'pandas', 'pickle']```
。__main__
的东西看起来更密集。
现在我们保存会话并退出Repl:
>>> dill.dump_session('session_01')
>>> exit()
当我们用' dill.load_session()'加载会话时会发生什么?
让我们打开另一个Repl来发现它:
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
Ok。只是另一个空的python解释器…
让我们加载会话,看看会发生什么:
>>> import dill
>>> dill.load_session('session_01')
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'datetime', 'dill', 'foo', 'functionWithUglyName', 'json', 'numpy', 'pandas', 'pickle']
按预期加载__main__
的内容。等一下。它加载了我们之前定义的functionWithUglyName
。这是真的吗?
>>> functionWithUglyName()
yep
原来迪尔真的很擅长连载东西。大多数情况下,您只需要pickle一些数据,但莳萝可以做更多……它非常适合调试和测试。