Python 习语“if __name__ == '__main__”的 clojure 等价物是什么?
我正在涉足 clojure,并且在尝试确定与这种常见 python 习惯用法等效的 clojure(和/或 Lisp)时遇到了一些麻烦。
习惯用法是,在 python 模块的底部通常有一些测试代码,然后是运行该代码的语句,例如:
# mymodule.py
class MyClass(object):
"""Main logic / code for the library lives here"""
pass
def _runTests():
# Code which tests various aspects of MyClass...
mc = MyClass() # etc...
assert 2 + 2 == 4
if __name__ == '__main__': _runTests()
这对于简单的临时测试很有用。 通常通过编写 from mymodule import MyClass
来使用此模块,在这种情况下,永远不会调用 _runTests()
,但通过末尾的代码片段,也可以运行它直接从命令行输入 python mymodule.py
。
Clojure(和/或 common lisp)中是否有等效的习惯用法? 我并不是在追求一个成熟的单元测试库(嗯,我是,但不是在这个问题中),我只想在模块中包含一些代码,这些代码只会在某些情况下运行,这样我就可以一种快速运行我一直在研究的代码的方法,但仍然允许像普通模块/命名空间一样导入我的文件。
I'm dabbling in clojure and am having a little trouble trying to determine the clojure (and / or Lisp) equivalent of this common python idiom.
The idiom is that at the bottom of a python module there is often a bit of test code, and then a statement which runs the code, for example:
# mymodule.py
class MyClass(object):
"""Main logic / code for the library lives here"""
pass
def _runTests():
# Code which tests various aspects of MyClass...
mc = MyClass() # etc...
assert 2 + 2 == 4
if __name__ == '__main__': _runTests()
This is useful for simple, ad-hoc testing. One would normally use this module by writing from mymodule import MyClass
, in which case _runTests()
is never called, but with the snippet at the end, one can also run it by typing python mymodule.py
directly from the command line.
Is there an equivalent idiom in Clojure (and/or common lisp)? I'm not after a full-blown unit testing library (well, I am, but not in this question), I'd just like to include some code in a module which will only be run under some circumstances, so I can have a quick way to run code I've been working on but still allow my file to be imported like a normal module / namespace.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
我对 Clojure 很陌生,但我认为 关于 Clojure 组的讨论可能是一种解决方案和/或解决方法,特别是 Stuart Sierra 在 4 月 17 日晚上 10:40 发表的帖子。
I'm very new to Clojure but I think this discussion on the Clojure groups may be a solution and/or workaround, specifically the post by Stuart Sierra on April 17th at 10:40 PM.
在 Common Lisp 中,您可以通过功能使用条件读取。
仅当功能列表绑定到 cl:*features* 将包含符号 :testing 。
例如,
将暂时添加 :testing 到功能列表中。
您可以定义自己的功能并控制 Common Lisp 系统读取哪些表达式以及跳过哪些表达式。
此外,您还可以执行以下操作:
In Common Lisp you can use conditional reading with features.
Above will only be read and thus execute during load if the list of features bound to cl:*features* will contain the symbol :testing .
For example
will temporarily add :testing to the list of features.
You can define your own features and control which expressions the Common Lisp system reads and which it skips.
Additionally you can also do:
http://rosettacode.org/wiki/Scripted_Main#Clojure 上还有一个不同可能性的列表。 (如果您发现新的 - 请添加它。;-))
There's also a list of different possibilities at http://rosettacode.org/wiki/Scripted_Main#Clojure. (If you find a new one - please add it. ;-))
您可能想查看test-is 来自 clojure-contrib 的库。 这不是同一个习惯用法,但它应该支持非常相似的工作流程。
You might want to have a look at the test-is library from clojure-contrib. It's not the same idiom, but it should support a pretty similar workflow.
Common Lisp 和 Clojure(以及其他 Lisp)提供了 REPL 的交互环境,并且您不需要像“
if __name__ == '__main__'
”这样的技巧。 python 有类似 REPL 的环境:命令行中的 python、ipython、Emacs 的 python 模式等。您应该只创建库,向其中添加一个测试套件(Common Lisp 有很多测试框架;我更喜欢5am 框架,有一个可用框架的调查此处)。 然后加载库,在 REPL 中您可以对库执行任何操作:运行测试、调用函数、实验等。
当您发现失败的测试时,您可以对其进行修复,重新编译更改的代码,然后继续进行实验,运行测试而无需重新启动整个应用程序。 这可以节省大量时间,因为正在运行的应用程序可能已经积累了很多状态(它可能已经创建了 GUI 窗口、连接到数据库、达到了某个不易重现的关键时刻),并且您不必重新启动它每次改变之后。
下面是 Common Lisp 的一个示例(来自我的 cl-sqlite 库):
代码:
和交互式会话:
现在假设我在 sqlite:execute-to-list 中发现了错误。 我查看该函数的代码,修复错误并重新编译该函数。 然后我调用固定函数并确保它有效。 内存数据库并没有消失,它的状态与重新编译之前相同。
Common Lisp and Clojure (as well as other lisps) provide interactive environment with REPL, and you do not need tricks like «
if __name__ == '__main__'
». There are REPL-like environments for python: the python from command-line, ipython, python mode for Emacs, etc.You should just create the library, add a testsuite to it (there are many testing frameworks for Common Lisp; I prefer the 5am framework, there is a survey of frameworks available here). And then you load the library, and in the REPL you can do anything with the library: run tests, call functions, experiment, etc.
When you find a failing test, you make a fix to it, recompile the changed code, and continue experimenting, running tests without restarting the whole application. This saves a lot of time, because the running application might have accumulated a lot of state (it might have created gui windows, connected to databases, reached some critical moment that is not easily reproduceable), and you don't have to restart it after every change.
Here's an example for Common Lisp (from my cl-sqlite library):
The code:
and the interactive session:
Now suppose that I found bug in sqlite:execute-to-list. I go to the code of this function, fix the bug and recompile this function. Then I call the fixed function and ensure that it works. The in-memory database is not gone, it has the same state as it had before recompile.
Boot 是一个构建工具(leiningen 的替代品),支持脚本。 因此,您可以有一个以
#!/usr/bin/env boot
开头的启动脚本,该脚本可以有一个-main
方法。您还可以从命令行调用任务,从而调用代码的不同函数。 您可以有一个打包任务,可以为这些函数之一创建一个 uberjar 作为入口点。
Boot is a build tooling (an alternative to leiningen), that supports scripts. So you could have a boot script beginning with
#!/usr/bin/env boot
that can have a-main
method.You could also make tasks invoked from the command line that would call up different functions of your code. And you could have a packaging task that can create an uberjar for one of these functions as entry points.
如果您正在谈论有一个“入口点”,您当然可以这样做:
现在会发生的情况是,任何时候此代码是 (load-file "foo.clj")'d 或 (use 'foo) 或 (require ' foo),然后 (main) 将被调用,这通常不会完成。
更常见的是,可以在 REPL 处加载代码文件,然后用户将调用主函数。
If you are talking about having an "entry point" you can certainly do that:
what will happen now is that any time this code is (load-file "foo.clj")'d or (use 'foo) or (require 'foo), then (main) will be called, that's usually not done.
Much more common is that a file of code can be loaded at the REPL and then the main function would be called by the user.
从命令行一遍又一遍地运行 Clojure 脚本并不符合习惯。 REPL 是一个更好的命令行。 Clojure 是一个 Lisp,通常会启动 Clojure 并让同一个实例永远运行,并与其交互而不是重新启动它。 您可以一次更改正在运行的实例中的一个函数,运行它们并根据需要插入它们。 摆脱繁琐而缓慢的传统编辑/编译/调试周期是 Lisps 的一大特色。
您可以轻松编写函数来执行诸如运行单元测试之类的操作,并且只要您想运行它们就可以从 REPL 调用这些函数,否则忽略它们。 在 Clojure 中,通常使用 clojure.contrib.test-is,将测试函数添加到命名空间,然后使用 clojure.contrib.test-is/run-tests 来运行它们全部。
不从命令行运行 Clojure 的另一个充分理由是 JVM 的启动时间可能会令人望而却步。
如果您确实想从命令行运行 Clojure 脚本,有多种方法可以实现。 请参阅Clojure 邮件列表了解一些讨论。
一种方法是测试命令行参数是否存在。 给定当前目录中的
foo.clj
:根据您启动 Clojure 的方式,您将获得不同的行为。
如果您想了解其工作原理,请参阅 Clojure 源代码中的
src/clj/clojure/main.clj
。另一种方法是将代码编译为
.class
文件并从 Java 命令行调用它们。 给定一个源文件foo.clj
:创建一个目录来存储编译后的
.class
文件; 默认为./classes
。 您必须自己创建此文件夹,Clojure 不会创建它。 另请确保设置$CLASSPATH
以包含./classes
以及包含源代码的目录; 我假设 foo.clj 位于当前目录中。 因此,从命令行:在
classes
目录中,您现在将拥有一堆.class
文件。 从命令行调用代码(默认运行-main
函数):clojure.org。
It's not idiomatic to run Clojure scripts over and over from the command line. The REPL is a better command line. Clojure being a Lisp, it's common to fire up Clojure and leave the same instance running forever, and interact with it rather than restart it. You can change functions in the running instance one at a time, run them and poke them as needed. Escaping the tedious and slow traditional edit/compile/debug cycle is a great feature of Lisps.
You can easily write functions to do things like run unit tests, and just call those functions from the REPL whenever you want to run them and ignore them otherwise. It's common in Clojure to use
clojure.contrib.test-is
, add your test functions to your namespace, then useclojure.contrib.test-is/run-tests
to run them all.Another good reason not to run Clojure from the commandline is that the startup time of the JVM can be prohibitive.
If you really want to run a Clojure script from the command line, there are a bunch of ways you can do it. See the Clojure mailing list for some discussion.
One way is to test for the presence of command line arguments. Given this
foo.clj
in the current directory:You'll get different behavior depending how you start Clojure.
See
src/clj/clojure/main.clj
in the Clojure source if you want to see how this is working.Another way is to compile your code into
.class
files and invoke them from the Java command line. Given a source filefoo.clj
:Make a directory to store the compiled
.class
files; this defaults to./classes
. You must make this folder yourself, Clojure won't create it. Also make sure you set$CLASSPATH
to include./classes
and the directory with your source code; I'll assumefoo.clj
is in the current directory. So from the command line:In the
classes
directory you will now have a bunch of.class
files. To invoke your code from the command line (running the-main
function by default):There's a lot of information about compiling Clojure code on clojure.org.