如何创建一个可以直接从 shell 执行的程序(谐波和)?

Posted

技术标签:

【中文标题】如何创建一个可以直接从 shell 执行的程序(谐波和)?【英文标题】:How to create a program which can be executed directly from the shell (harmonic sum)? 【发布时间】:2021-06-10 16:47:45 【问题描述】:

我在 MatLab 中做过一些编程,但我对 Python 完全陌生。对于我们的第一个项目,我们的老师希望我们编写一个谐波和 (1/1+1/2+1/3...) 和一个“可以直接从 shell 执行的完整程序,如下所示:

$ python3 hsum.py 10 0 0 1 1.0 2 1.5 3 1.8333333333333333 4 2.083333333333333 5 2.283333333333333 6 2.4499999999999997 7 2.5928571428571425 8 2.7178571428571425 9 2.8289682539682537

我是这样管理的:

import sys

def hSum(n):
    s = 0
    for i in range(n + 1):
        if i == 0:
            s = 0
        else:
            s += 1 / i
    return s


def main():
    hSum(int(sys.argv[1])) # a command he has specified for us to use

    main()

主要问题是我实际上不明白他所说的“可以直接从shell执行的完整程序”是什么意思。显然第二个问题是,当我运行他提供的“测试”文件时,它会测试程序是否正确,它告诉我:

函数 main 应该产生与赋值完全相同的输出 得到: [空白] 而不是: [前面引用的列表]

这里是 test.py 代码:

import sys
import importlib.util


def testEq(res, ref, msg):
    global pass_tests, fail_tests
    if res == ref:
        pass_tests = pass_tests + 1
    else:
        print(msg)
        print("Got:")
        print(res)
        print("Instead of:")
        print(ref)
        fail_tests = fail_tests + 1


def test(fun, x, y):
    global pass_tests, fail_tests
    if type(x) == tuple:
        z = fun(*x)
    else:
        z = fun(x)
    if y == z:
        pass_tests = pass_tests + 1
    else:
        if type(x) == tuple:
            s = repr(x)
        else:
            s = "(" + repr(x) + ")"
        print("Condition failed:")
        print("   " + fun.__name__ + s + " == " + repr(y))
        print(fun.__name__ + " returned/printed:")
        print(str(z))
        fail_tests = fail_tests + 1


def run(src_path=None):
    global pass_tests, fail_tests

    saved_stdout = sys.stdout
    sys.stdout = io.StringIO()

    saved_argv = sys.argv
    sys.argv = ["hsum.py", "10"]

    if src_path == None:
        import hsum
    else:
        spec = importlib.util.spec_from_file_location("hsum", src_path + "/hsum.py")
        hsum = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(hsum)

    sys.argv = saved_argv

    out = sys.stdout.getvalue()
    sys.stdout = saved_stdout

    pass_tests = 0
    fail_tests = 0
    fun_count = 0

    if hasattr(hsum, "hSum"):
        fun_count = fun_count + 1
        test(hsum.hSum, 5, 2.283333333333333)
        test(hsum.hSum, 7, 2.5928571428571425)
    else:
        print("hSum is not implemented yet!")

    if hasattr(hsum, "main"):
        fun_count = fun_count + 1
        testEq(out,
               "0 0\n1 1.0\n2 1.5\n3 1.8333333333333333\n4 2.083333333333333\n5 2.283333333333333\n6 "
               "2.4499999999999997\n7 2.5928571428571425\n8 2.7178571428571425\n9 2.8289682539682537\n",
               "function main should generate exactly the same output as in the assignment")
    else:
        print("main is not implemented yet!")

    print(str(pass_tests) + " out of " + str(pass_tests + fail_tests) + " passed.")

    return fun_count == 2 and fail_tests == 0


if __name__ == "__main__":
    run()




【问题讨论】:

你有没有试过你说的我已经这样管理了 【参考方案1】:

现在您的代码不会打印或返回任何内容。这可能是复制/粘贴中的错误 - 如果您取消缩进最后一行 main() 然后它会运行并且 hSum() 在写入代码时返回单个值 s。现在,这最后一行作为main() 函数本身的一部分执行(导致它无限循环),但函数本身从未真正被调用过,所以什么也没有发生。

你能上传一份 test.py 脚本的副本吗?尚不清楚它期望如何返回结果。

从第一个引用来看,它只是想让你打印所有的值。您可以通过在for 循环中添加print(str(i) + " " + str(s)) 轻松完成此操作(当然是在计算s 之后)。你不需要return s,因为这会导致hSum()“等于”s(例如value = hSum()会导致value获取函数返回的值),但你不是用这些信息做任何事情。

使用以下代码,我通过了 2/3 次测试。我稍后会解释原因。

import sys

def hSum(n):
    s = 0
    for i in range(n + 1):
        if i == 0:
            s = 0
        else:
            s += 1 / i
        print(str(i) + " " + str(s))
    return s


def main():
    hSum(int(sys.argv[1])) # a command he has specified for us to use

main()

使用return s 是正确的做法,因为它允许测试脚本调用hSum(n) 函数并获得s 的最终值。这适用于测试 1 和 2,为 5 和 7 返回正确的值。

我认为第三个测试有一个错误,或者是我看不到的测试代码实现方式的核心差异。此问题会导致冲突 - 在测试 1 和 2 中,它希望函数返回 5 和 7 的值包括 5 和 7。对于最终测试,它希望它返回 10 包括 10 的值,但打印 不包括 10 的值。可以为此编写一个解决方法以通过测试,但是虽然是的 - 你通过了任意测试 - 代码将不会像你保持原样那样有效。这是测试驱动开发的常见问题。您的测试需要完美才能产生好的代码。

除非其他人有我想念的解释,否则我建议给你的导师/讲师发消息并询问这个冲突。

这里的 FWIW 是一种解决方法,它根本不打印最终结果。我不推荐这样的编码,但它通过了所有的测试。

import sys

def hSum(n):
    s = 0
    for i in range(n+1):
        
        if i == 0:
            s = 0
        else:
            s += 1 / i
        if(i != n):
            print(str(i) + " " + str(s))
    return s


def main():
    hSum(int(sys.argv[1])) # a command he has specified for us to use

main()

【讨论】:

谢谢,我已经更新了帖子以包含 test.py。我没有缩进main(),但现在它给了我信息:Traceback (most recent call last):\ File "xxx", line 16, in <module> main()\ File "xxx", line 15, in main\ hSum(int(sys.argv[1]))\ IndexError: list index out of range @Cacti 我在答案中添加了对我有用的代码,将查看测试代码并查看它到底期待什么。 @Cacti - 您自己在问题的第一段中写道可以像这样直接从 shell 执行的完整程序:... - 现在您只需要继续然后像这样 @Cacti 再次阅读您的评论,此错误意味着 sys.argv 参数列表中没有“1”元素。此列表是您在运行脚本时在“python”之后编写的内容列表 - 其中 sys.argv[0] 是脚本的路径。 [1] 超出范围表明您没有在路径后添加要计算的数字。【参考方案2】:

我同意 yuuuu 的观点,看到更多可能会有所帮助。

还有一个叫做 shebang (#!/usr/bin/env python3) 的东西,您可以将它放在文件的顶部以使其也可执行。它应该是文件中的第一行(在导入或任何内容之前)。它告诉操作系统使用什么来执行文件。它看起来像下面的代码:

#!/usr/bin/env python3
import sys


def hSum(n):
    s = 0
...

正如 yuuuu 指出的那样,您调用了 main(),但由于它的缩进,看起来您正在调用 main() 函数本身。您可以只调用 main() 就像您正在尝试做的那样(但使用正确的缩进),但还有另一种选择,name == 'ma​​in' if 语句。这是对 https://www.freecodecamp.org/news/if-name-main-python-example/ 的更深入的解释,但我的理解是它是从命令行执行 python 脚本时调用函数或 w/e 的标准方式

#!/usr/bin/env python3
import sys


def hSum(n):
    s = 0
    for i in range(n + 1):
        if i == 0:
            s = 0
        else:
            s += 1 / i
    return s


def main():
    hSum(int(sys.argv[1])) # a command he has specified for us to use

if __name__ == '__main__':
    main()

编辑// 如果没有 yuuuu 询问的信息,很难给出更多答案。

【讨论】:

谢谢,我已经更新了帖子以包含 test.py 我想让这段代码尽可能基本,因为这是我们的第一个任务

以上是关于如何创建一个可以直接从 shell 执行的程序(谐波和)?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Flask App 执行 Shell 脚本 [重复]

如何从 shell 执行 XPath 单行程序?

使用beeline shell拒绝用户匿名的Hive权限

如何写一个shell脚本在Linux下来运行main函数

如何给shell脚本传参数

如何在C语言中执行shell命令