Python 习语“if __name__ == '__main__”的 clojure 等价物是什么?

发布于 2024-07-24 03:27:43 字数 761 浏览 3 评论 0原文

我正在涉足 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

沦落红尘 2024-07-31 03:27:44

我对 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.

许久 2024-07-31 03:27:44

在 Common Lisp 中,您可以通过功能使用条件读取。

#+testing (run-test 'is-answer-equal-42)

仅当功能列表绑定到 cl:*features* 将包含符号 :testing 。

例如,

(let ((*features* (cons :testing *features*)))
   (load "/foo/bar/my-answerlib.lisp"))

将暂时添加 :testing 到功能列表中。

您可以定义自己的功能并控制 Common Lisp 系统读取哪些表达式以及跳过哪些表达式。

此外,您还可以执行以下操作:

#-testing (print '|we are in production mode|)

In Common Lisp you can use conditional reading with features.

#+testing (run-test 'is-answer-equal-42)

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

(let ((*features* (cons :testing *features*)))
   (load "/foo/bar/my-answerlib.lisp"))

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:

#-testing (print '|we are in production mode|)
木有鱼丸 2024-07-31 03:27:44

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. ;-))

无人问我粥可暖 2024-07-31 03:27:44

您可能想查看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.

往日 2024-07-31 03:27:44

Common Lisp 和 Clojure(以及其他 Lisp)提供了 REPL 的交互环境,并且您不需要像“if __name__ == '__main__'”这样的技巧。 python 有类似 REPL 的环境:命令行中的 python、ipython、Emacs 的 python 模式等。

您应该只创建库,向其中添加一个测试套件(Common Lisp 有很多测试框架;我更喜欢5am 框架,有一个可用框架的调查此处)。 然后加载库,在 REPL 中您可以对库执行任何操作:运行测试、调用函数、实验等。

当您发现失败的测试时,您可以对其进行修复,重新编译更改的代码,然后继续进行实验,运行测试而无需重新启动整个应用程序。 这可以节省大量时间,因为正在运行的应用程序可能已经积累了很多状态(它可能已经创建了 GUI 窗口、连接到数据库、达到了某个不易重现的关键时刻),并且您不必重新启动它每次改变之后。

下面是 Common Lisp 的一个示例(来自我的 cl-sqlite 库):

代码:

(def-suite sqlite-suite)

(defun run-all-tests ()
  (run! 'sqlite-suite));'

(in-suite sqlite-suite)

(test test-connect
  (with-open-database (db ":memory:")))

(test test-disconnect-with-statements
  (finishes
    (with-open-database (db ":memory:")
      (prepare-statement db "create table users (id integer primary key, user_name text not null, age integer null)"))))
...

和交互式会话:

CL-USER> (sqlite-tests:run-all-tests)
.......
 Did 7 checks.
    Pass: 7 (100%)
    Skip: 0 ( 0%)
    Fail: 0 ( 0%)

NIL
CL-USER> (defvar *db* (sqlite:connect ":memory:"))
*DB*
CL-USER> (sqlite:execute-non-query *db* "create table t1 (field text not null)")
; No value
CL-USER> (sqlite:execute-non-query *db* "insert into t1 (field) values (?)" "hello")
; No value
CL-USER> (sqlite:execute-to-list *db* "select * from t1")
(("hello"))
CL-USER> 

现在假设我在 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:

(def-suite sqlite-suite)

(defun run-all-tests ()
  (run! 'sqlite-suite));'

(in-suite sqlite-suite)

(test test-connect
  (with-open-database (db ":memory:")))

(test test-disconnect-with-statements
  (finishes
    (with-open-database (db ":memory:")
      (prepare-statement db "create table users (id integer primary key, user_name text not null, age integer null)"))))
...

and the interactive session:

CL-USER> (sqlite-tests:run-all-tests)
.......
 Did 7 checks.
    Pass: 7 (100%)
    Skip: 0 ( 0%)
    Fail: 0 ( 0%)

NIL
CL-USER> (defvar *db* (sqlite:connect ":memory:"))
*DB*
CL-USER> (sqlite:execute-non-query *db* "create table t1 (field text not null)")
; No value
CL-USER> (sqlite:execute-non-query *db* "insert into t1 (field) values (?)" "hello")
; No value
CL-USER> (sqlite:execute-to-list *db* "select * from t1")
(("hello"))
CL-USER> 

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.

惯饮孤独 2024-07-31 03:27:44

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.

萌逼全场 2024-07-31 03:27:44

如果您正在谈论有一个“入口点”,您当然可以这样做:

(ns foo)

(defn foo [n]
  (inc n))

(defn main []
  (println "working")
  (println "Foo has ran:" (foo 1)))

(main)

现在会发生的情况是,任何时候此代码是 (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:

(ns foo)

(defn foo [n]
  (inc n))

(defn main []
  (println "working")
  (println "Foo has ran:" (foo 1)))

(main)

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.

迷离° 2024-07-31 03:27:43

从命令行一遍又一遍地运行 Clojure 脚本并不符合习惯。 REPL 是一个更好的命令行。 Clojure 是一个 Lisp,通常会启动 Clojure 并让同一个实例永远运行,并与其交互而不是重新启动它。 您可以一次更改正在运行的实例中的一个函数,运行它们并根据需要插入它们。 摆脱繁琐而缓慢的传统编辑/编译/调试周期是 Lisps 的一大特色。

您可以轻松编写函数来执行诸如运行单元测试之类的操作,并且只要您想运行它们就可以从 REPL 调用这些函数,否则忽略它们。 在 Clojure 中,通常使用 clojure.contrib.test-is,将测试函数添加到命名空间,然后使用 clojure.contrib.test-is/run-tests 来运行它们全部。

不从命令行运行 Clojure 的另一个充分理由是 JVM 的启动时间可能会令人望而却步。

如果您确实想从命令行运行 Clojure 脚本,有多种方法可以实现。 请参阅Clojure 邮件列表了解一些讨论。

一种方法是测试命令行参数是否存在。 给定当前目录中的 foo.clj

(ns foo)

(defn hello [x] (println "Hello," x))

(if *command-line-args*
  (hello "command line")
  (hello "REPL"))

根据您启动 Clojure 的方式,您将获得不同的行为。

$ java -cp ~/path/to/clojure.jar:. clojure.main foo.clj --
Hello, command line
$ java -cp ~/path/to/clojure.jar:. clojure.main
Clojure 1.1.0-alpha-SNAPSHOT
user=> (use 'foo)
Hello, REPL
nil
user=>

如果您想了解其工作原理,请参阅 Clojure 源代码中的 src/clj/clojure/main.clj

另一种方法是将代码编译为 .class 文件并从 Java 命令行调用它们。 给定一个源文件foo.clj

(ns foo
  (:gen-class))

(defn hello [x] (println "Hello," x))

(defn -main [] (hello "command line"))

创建一个目录来存储编译后的.class文件; 默认为./classes。 您必须自己创建此文件夹,Clojure 不会创建它。 另请确保设置 $CLASSPATH 以包含 ./classes 以及包含源代码的目录; 我假设 foo.clj 位于当前目录中。 因此,从命令行:

$ mkdir classes
$ java -cp ~/path/to/clojure.jar:./classes:. clojure.main
Clojure 1.1.0-alpha-SNAPSHOT
user=> (compile 'foo)
foo

classes 目录中,您现在将拥有一堆 .class 文件。 从命令行调用代码(默认运行 -main 函数):

$ java -cp ~/path/to/clojure.jar:./classes foo
Hello, command line.

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 use clojure.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:

(ns foo)

(defn hello [x] (println "Hello," x))

(if *command-line-args*
  (hello "command line")
  (hello "REPL"))

You'll get different behavior depending how you start Clojure.

$ java -cp ~/path/to/clojure.jar:. clojure.main foo.clj --
Hello, command line
$ java -cp ~/path/to/clojure.jar:. clojure.main
Clojure 1.1.0-alpha-SNAPSHOT
user=> (use 'foo)
Hello, REPL
nil
user=>

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 file foo.clj:

(ns foo
  (:gen-class))

(defn hello [x] (println "Hello," x))

(defn -main [] (hello "command line"))

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 assume foo.clj is in the current directory. So from the command line:

$ mkdir classes
$ java -cp ~/path/to/clojure.jar:./classes:. clojure.main
Clojure 1.1.0-alpha-SNAPSHOT
user=> (compile 'foo)
foo

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):

$ java -cp ~/path/to/clojure.jar:./classes foo
Hello, command line.

There's a lot of information about compiling Clojure code on clojure.org.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文