是否可以在 Python 中使用与 Smalltalk(例如 Pharo)或 Common Lisp 中类似的工作流程?

Is it possible to have a similar workflow in Python as in Smalltalk (e.g. Pharo) or Common Lisp?

我非常喜欢在 Smalltalk 中可以进行的增量编程。您有一个 运行 程序,您可以在充实程序时向其中添加内容。您可以更改方法并在应用更改后重新启动堆栈以查看新版本的功能。当您的程序是 运行 时,您可以检查本地状态并更改它们。

在 Python 中是否有类似的东西?我已经看到了这种能力的暗示,例如 reload(),但我对 Python 的了解还不够,无法准确理解它的使用方式。我浏览了一些初学者 Python 书籍,但没有看到任何提及。

您可以即时更改函数定义。

例如,您有一个函数 mymodule.myfunc(x,y),您想要查看 long_process() 是如何调用它的。

你这样做(在 REPL >>> 提示符下或在笔记本中)

myfunc_orig = mymodule.myfunc
def myfunc_new(x,y):
    print("myfunc_new",x,y)
    return myfunc_orig(x,y)

mymodule.myfunc = myfunc_new

long_process()

现在每次调用 mymodule.myfunc 时都会打印输出。

完成后,您可以使用

恢复它
mymodule.myfunc = myfunc_orig

我使用 ipython 进行交互模式 - 并且在编程项目完成之前保持终端打开,或者您可以通过 dill 保存会话(在 ipython 控制台中执行:!pip install dill).

使用 dill 包保存会话

要保存所有全局变量和定义,请执行以下操作:

import dill       
dill.dump_session('.session.pkl')

在新会话中您可以通过以下方式加载:

import dill
dill.load_session('.session.pkl')

取自here

有些事情是再怎么努力也做不到的Python。例如,在开发网络应用程序时,Flask/Django/Gunicorn 或诸如此类的网络服务器必须在源代码更改后重新启动其进程。但是在 Lisp 中,你在 REPL 中启动了一个 Web 服务器,你只需编译一个函数,例如添加一个新路由,你就可以立即尝试。没有重启任何进程,一切都更具交互性。

另一个示例是更新 classes 和实例。在 Common Lisp 中,假设你写了一个 class 并创建了一些对象。现在您更改 class 定义,并且现有实例得到(延迟)更新。例如,添加一个新插槽,删除一个插槽等。我们甚至可以控制更新的完成方式(通过子class一些通用函数)。

在Python中附加一个运行和远程进程是可以的,但是交互性要差很多,编辑体验也不太理想(愚蠢的python shell 在终端中默认 VS 一个完整的 Emacs,你可以在其中导航源代码并通过一次击键重新编译函数(C-c C-c 在 Slime 中)(或在任何其他可以连接到 Swank 服务器的编辑器中) ).

运行 一个给定的单元测试也很直接和快速,没有重新启动的过程。

参考文献:

内置方法exec(source_code, globals, locals) 接受 'globals' 和 'locals' 参数。 这些非常接近执行 'environment',或来自 smalltalk 的 'execution image'。

exec(..) 调用返回时,将声明的 class/function/variable 添加到 globals/locals。

globals/locals 可以很容易地存储到一个文件中,并用作未来执行的起始环境:)

模拟 smalltalk 'image based'。

我想这个技巧可以重现 smalltalk 执行环境的属性,是真的吗?

示例:

locals, globals = {}, {}
exec("""def f(x): return x+1""", globals, locals)
print(locals["f"](2))

exec("""def f(x): return x+10""", globals, locals)
print(locals["f"](2))