我非常喜欢在Smalltalk中可以进行的增量编程。你有一个正在运行的程序,当你充实你的程序时,你会添加到它中。您可以更改方法,并使用所应用的更改重新启动堆栈,以查看新版本的功能。当程序运行时,您可以检查本地状态并更改它们。
类似的东西在Python中可能存在吗?我已经看到了这种能力的暗示,比如reload((,但我对Python的了解还不够,无法确切理解它是如何使用的。我看过一些Python初学者的书,但没有看到任何提到这一点。
无论在Python上付出什么努力,有些事情都是不可能的。例如,在开发web应用程序时,Flask/Django/Gunicorn或whatnot web服务器必须在源代码发生更改后重新启动其进程。但在Lisp中,您在REPL中启动一个web服务器,只需编译一个函数,例如添加一个新路由,就可以立即尝试。没有重新启动任何进程,它更具互动性。
另一个例子是更新类和实例。在Common Lisp中,假设您编写了一个类并创建了一些对象。现在您更改了类定义,并且现有实例得到了(延迟(更新。例如,添加一个新插槽,删除一个插槽,等等。我们甚至可以控制更新的方式(通过子类化一些通用函数(。
在Python中附加到一个正在运行的远程进程是可行的,但交互性要低得多,编辑体验也不太理想(默认情况下,终端中的一个愚蠢的Python shell与一个完整的Emacs相比,在这里你可以一键导航源代码并重新编译函数(Slime中的C-c C-c
((或在任何其他可以连接到Swank服务器的编辑器中((。
运行一个给定的单元测试也是简单而快速的,没有重新启动的过程。
参考文献:
- https://lispcookbook.github.io/cl-cookbook/clos.html
- 在另一台计算机上调试正在运行的程序:https://lispcookbook.github.io/cl-cookbook/debugging.html#remote-调试
我使用ipython进行交互模式,并且在编程项目完成之前保持终端打开,或者您可以通过dill保存会话(在ipython控制台do:!pip install dill
中(。
使用dill包保存会话
要保存所有全局变量和定义,请执行以下操作:
import dill
dill.dump_session('.session.pkl')
在新会话中,您可以通过以下方式加载:
import dill
dill.load_session('.session.pkl')
它是从这里
您可以随时更改函数定义。
例如,您有一个函数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
内置方法exec(source_code, globals, locals)
接受"globals"one_answers"locals"参数。这些非常接近smalltalk中的执行"环境"或"执行图像"。
声明的类/函数/变量在从exec(..)
调用返回时被添加到全局/本地。
全局/本地可以很容易地存储到一个文件中,并用作未来执行的启动环境:(
这模拟了"基于图像"的闲聊。
我想这个技巧可以重现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))