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

时间:2009-06-10 00:07:59

标签: python lisp clojure idioms

我正在考虑使用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(和/或普通的lisp)中是否存在等价的习惯用法?我不是在一个完整的单元测试库之后(好吧,我,但不是在这个问题中),我只想在一个模块中包含一些代码,这些代码只能在某些情况下运行,所以我可以拥有一种快速运行代码的方法我一直在努力,但仍允许我的文件像普通的模块/命名空间一样导入。

8 个答案:

答案 0 :(得分:27)

从命令行反复运行Clojure脚本并不是惯用的。 REPL是一个更好的命令行。 Clojure是一个Lisp,通常会激活Clojure并使相同的实例永远运行,并与之交互而不是重新启动它。您可以一次更改正在运行的实例中的函数,运行它们并根据需要进行戳。逃避繁琐而缓慢的传统编辑/编译/调试周期是Lisps的一大特色。

您可以轻松编写函数来执行运行单元测试等操作,只需在运行它们时从REPL调用这些函数,否则忽略它们。在Clojure中使用clojure.contrib.test-is,将测试函数添加到命名空间,然后使用clojure.contrib.test-is/run-tests来运行它们是很常见的。

不从命令行运行Clojure的另一个好理由是JVM的启动时间可能过高。

如果您真的想从命令行运行Clojure脚本,可以通过多种方式执行此操作。有关讨论,请参阅the Clojure mailing list

一种方法是测试是否存在命令行参数。在当前目录中给出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上编译Clojure代码的信息。

答案 1 :(得分:1)

我对Clojure很新,但我认为Clojure小组的this discussion可能是一个解决方案和/或解决方法,特别是Stuart Sierra在4月17日晚上10:40的帖子。

答案 2 :(得分:1)

在Common Lisp中,您可以使用features条件阅读。

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

如果绑定到cl:*features*的要素列表包含符号:testing。

,则只会读取上面的内容,从而在加载期间执行。

例如

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

将临时添加:测试到功能列表。

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

此外,您还可以:

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

答案 3 :(得分:1)

http://rosettacode.org/wiki/Scripted_Main#Clojure还有一系列不同的可能性。 (如果你找到一个新的 - 请加上它。; - ))

答案 4 :(得分:0)

您可能想要查看来自clojure-contrib的test-is库。它不是同一个成语,但它应该支持一个非常相似的工作流程。

答案 5 :(得分:0)

Common Lisp和Clojure(以及其他lisps)提供了REPL的交互式环境,你不需要像«if __name__ == '__main__'»这样的技巧。 python有类似REPL的环境:来自命令行的python,用于Emacs的ipython,python模式等。

你应该创建库,为它添加一个测试套件(Common Lisp有很多测试框架;我更喜欢5am框架,有一个可用的框架调查here)。然后加载库,在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中发现了bug:execute-to-list。我转到这个函数的代码,修复bug并重新编译这个函数。然后我调用固定功能并确保它工作。内存数据库没有消失,它具有与重新编译之前相同的状态。

答案 6 :(得分:0)

Boot是一个构建工具(leiningen的替代品),supports scripts。因此,您可以使用#!/usr/bin/env boot开头的启动脚本,该脚本可以使用-main方法。

您还可以从命令行调用任务来调用代码的不同函数。你可以有一个打包任务,可以为其中一个函数创建一个uberjar作为入口点。

答案 7 :(得分:-3)

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

(ns foo)

(defn foo [n]
  (inc n))

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

(main)

现在会发生的是,只要这段代码是(load-file“foo.clj”)'d或(使用'foo)或(require'foo),那么(main)将被调用,这通常不会完成。

更常见的是,可以在REPL中加载代码文件,然后用户将调用main函数。