将 Flask/Dill 集成到 dump/load 服务器会话

Integrating Flask/Dill to dump/load server sessions

我正在尝试将 Flask 与 Dill 集成到服务器端的 dump/load 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 因为它很简单,但您也可以为此定义 class。

简单(且乏味)的方法是在每次需要应用程序状态时调用 state = dill.load('filename')dill.dump(object,'filename')

如果您的应用程序很小,这将有效。如果您需要维护适当的应用程序状态,您应该使用数据库。

好的。但是这里发生了什么?

dill 和 Flask 没有兼容性问题。

当您调用 dill.dump_session() 时,它会保存 __main__ 的状态。

但是,当你在函数 count() 中增加 x 时,它是未定义的,因为它没有被 dill 保存。

一个简单的方法是在 x = x + 1 之前放置一个 breakpoint() 或打印 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__']

好的。只是另一个空 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

原来 dill 真的很擅长序列化东西。 大多数时候你只需要 pickle 一些数据,但 dill 可以做更多......而且它非常适合调试和测试。