从命令行运行函数

559

我有这段代码:

def hello():
    return 'Hi :)'

我该如何直接从命令行运行此程序?


38
可能你的意思是要写print "Hi :)"而不是return 'Hi :)' - Tamás
19个回答

837

如果你的文件名为foo.py,可以使用-c(命令)参数:

$ python -c 'import foo; print foo.hello()'

或者,如果您不关心命名空间污染:

$ python -c 'from foo import *; print hello()'

还有一个折中方案:

$ python -c 'from foo import hello; print hello()'

58
我注意到在Windows shell上,需要用双引号而不是单引号。$python -c "import foo;foo.hello()" - Arindam Roychowdhury
7
如果文件不在本地目录或PYTHONPATH上,该怎么办? - Konstantin
8
由于某些原因,这对我不起作用,而将print foo.hello()替换为print(foo.hello())则可以。我没有足够的Python知识来解释这是为什么,如果有人能够解释一下可能发生了什么,那将不胜感激。 - Jasper
16
尽管这篇答案是十年前写的,但今天它却是谷歌搜索的顶部结果。我认为这就是这个网站的目的所在:一个找到相关答案的地方,而不是对语言演变的见证。常见问题(FAQ)实际上提到,将帖子保持最新状态是进行编辑的重要原因之一。我认为在这个答案中添加一节关于Python版本的内容将是更好的方式来确认语言的变化,但我认为将代码更改为跨版本兼容的解决方案更加优雅。而编辑历史记录将永远作为证明。 - Jasper
5
这太荒谬了。这个答案有超过700个赞,但在任何当前支持的Python版本上都无法工作。请接受这些微小的编辑,它们实际上修复了错误的代码。 - Eric Duminil
显示剩余11条评论

170

只需要在函数下方添加hello(),然后当你执行python your_file.py时它就会被执行。

如果想要更简洁的解决方案,可以使用以下代码:

if __name__ == '__main__':
    hello()

这样,只有在运行文件时才会执行该函数,而不是在导入文件时执行。


6
如果hello()需要由命令行提供参数,该怎么办? - pretzlstyle
3
在这种情况下,您可以将sys.argv发送到该方法。或者从hello方法中访问它。 - Wolph
3
使用 import foo 的解决方案和这个答案之间的一个区别是,import foo 允许在不修改 foo 的情况下调用 foo 中的任意函数。 - plafratt
没错,但我不建议在测试目的以外的情况下使用那个解决方案。 - Wolph
@Wolph 嘿,使用这个结构,我该如何执行一个单独的函数(不包含在 hello() 中)并从命令行运行它? - Souvik Ray
你不能直接这样做。你可以使用 sys.argv 解析(例如使用 argparse)来执行不同的方法。 - Wolph

100

将此片段添加到您的脚本底部

def myfunction():
    ...


if __name__ == '__main__':
    globals()[sys.argv[1]]()

现在你可以通过运行来调用你的函数

python myscript.py myfunction
这是因为您将命令行参数(函数名称的字符串)传递到具有当前本地符号表的字典locals中。在末尾的括号将调用该函数。更新:如果您想让函数从命令行接受参数,可以像这样传递sys.argv[2]
def myfunction(mystring):
    print(mystring)


if __name__ == '__main__':
    globals()[sys.argv[1]](sys.argv[2])

这样,运行python myscript.py myfunction "hello"会输出hello


1
这个方法能否接受一个函数参数?比如 myfunction(12) - Major Major
1
@MajorMajor 我已经更新了答案,包括如何做到这一点。 - Noam Hacker
在生产环境中这样做会有危险吗?就像想把它作为单元测试一样。 - Ardhi
1
D. Jagatiya的答案提供了更完整的传递参数示例--它将所有的参数传递给函数,如果用户未传递正确数量的参数,则会得到合理的错误提示。 - idbrii
2
@Ardhi:我不会在生产环境中这样做,因为它允许调用文件中的任何全局函数,这使得代码变得脆弱。使用pytest进行简单的测试设置 - idbrii

81

python -c 'from 文件名 import hello; hello()' 这里需要将 文件名 替换为你的 Python 脚本的基本名称(例如,myfile.py 变成 myfile)。

但是,如果 hello() 是您的 Python 脚本中的“永久”主入口点,则通常的方法如下:

def hello():
    print "Hi :)"

if __name__ == "__main__":
    hello()

这使你可以通过运行python myfile.pypython -m myfile来简单地执行脚本。

一些解释: __name__是一个特殊的Python变量,它保存当前正在执行的模块的名称,除非该模块从命令行启动,在这种情况下它变成"__main__"


2
python -m foo -c 'foo.bar()'python -c 'import foo; foo.bar()' 有什么区别?我发现它们的行为不同,似乎第一种情况下 -c 参数被忽略了。 - Abram
@Abram -m mod:将库模块作为脚本运行(终止选项列表) - bers

36

我写了一个简短的 Python 脚本,可以从 bash 命令行中调用。它接受要调用的模块、类和方法的名称,以及您想要传递的参数。我称之为 PyRun,并省略了 .py 扩展名并使用 chmod +x PyRun 命令将其设置为可执行文件,这样我就可以快速地调用它:

./PyRun PyTest.ClassName.Method1 Param1

将这个保存在名为PyRun的文件中

#!/usr/bin/env python
#make executable in bash chmod +x PyRun

import sys
import inspect
import importlib
import os

if __name__ == "__main__":
    cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0]))
    if cmd_folder not in sys.path:
        sys.path.insert(0, cmd_folder)

    # get the second argument from the command line      
    methodname = sys.argv[1]

    # split this into module, class and function name
    modulename, classname, funcname = methodname.split(".")

    # get pointers to the objects based on the string names
    themodule = importlib.import_module(modulename)
    theclass = getattr(themodule, classname)
    thefunc = getattr(theclass, funcname)

    # pass all the parameters from the third until the end of 
    # what the function needs & ignore the rest
    args = inspect.getargspec(thefunc)
    z = len(args[0]) + 2
    params=sys.argv[2:z]
    thefunc(*params)

以下是一个示例模块,展示其工作原理。该模块保存在名为PyTest.py的文件中:

class SomeClass:
 @staticmethod
 def First():
     print "First"

 @staticmethod
 def Second(x):
    print(x)
    # for x1 in x:
    #     print x1

 @staticmethod
 def Third(x, y):
     print x
     print y

class OtherClass:
    @staticmethod
    def Uno():
        print("Uno")

尝试运行这些例子:

./PyRun PyTest.SomeClass.First
./PyRun PyTest.SomeClass.Second Hello
./PyRun PyTest.SomeClass.Third Hello World
./PyRun PyTest.OtherClass.Uno
./PyRun PyTest.SomeClass.Second "Hello"
./PyRun PyTest.SomeClass.Second \(Hello, World\)

请注意,最后一个例子是通过转义括号将元组作为 Second 方法的唯一参数传递。

如果你传递的参数过少,那么会出现错误。如果你传递的参数太多,它会忽略额外的参数。该模块必须在当前工作文件夹中,但 PyRun 可以放在任何路径下。


2
很好,但这并不是问题的真正答案。 - Francis Colas
16
我不太同意;这正是问题所在。他问如何从一个文件中运行一个函数,这正是这个操作所做的事情。 - Joseph Gagliardo
你能解释一下 cmd_folder 是做什么的吗? - RyanDay

33

我们可以这样写。我使用的是python-3.7.x版本。

import sys

def print_fn():
    print("Hi")

def sum_fn(a, b):
    print(a + b)

if __name__ == "__main__":
    args = sys.argv
    # args[0] = current file
    # args[1] = function name
    # args[2:] = function args : (*unpacked)
    globals()[args[1]](*args[2:])

python demo.py print_fn
python demo.py sum_fn 5 8

15

让我们简单点,只需使用一个模块...

尝试:pip install compago

然后写:

import compago
app = compago.Application()

@app.command
def hello():
    print "hi there!"

@app.command
def goodbye():
    print "see ya later."

if __name__ == "__main__":
    app.run()

那么就像这样使用:

$ python test.py hello
hi there!

$ python test.py goodbye
see ya later.

注意:目前Python 3存在错误,但在Python 2上表现良好。

编辑:我认为更好的选择是Google的模块fire,它使传递函数参数也变得容易。可以使用pip install fire进行安装。以下是他们GitHub上的一个简单示例:

这里是一个简单的示例。

import fire

class Calculator(object):
  """A simple calculator class."""

  def double(self, number):
    return 2 * number

if __name__ == '__main__':
  fire.Fire(Calculator)

然后,你可以在命令行中运行:

python calculator.py double 10  # 20
python calculator.py double --number=15  # 30

2
+1. Fire甚至有一种在不更改脚本的情况下调用函数的方法:python -m fire file_name method_name。它还具有内置的argparser。 - user3265569

7

有趣的是,如果目标是将内容打印到命令行控制台或执行其他小型Python操作,可以像这样将输入管道传递给Python解释器:

echo print("hi:)") | python

除了管道文件之外...

python < foo.py

*注意第二个操作不一定需要.py扩展名才能工作。 **还请注意,对于bash,您可能需要转义字符。
echo print\(\"hi:\)\"\) | python

考虑到带有hello()示例的foo.py,以下是如何使用它与上述想法。echo import foo;foo.hello() | python - Arindam Roychowdhury
这个方法有没有办法传入命令行参数? - sparkonhdfs
1
顺便说一句,对于第三个示例,以下代码略微更加简洁:echo 'print("hi:)")' | python - user3166580

7

我有一个需求,需要在命令行上使用各种Python工具(如range、string等),并编写了pyfunc工具来满足这个需求。您可以使用它来丰富您的命令行使用体验:

 $ pyfunc -m range -a 1 7 2
 1
 3
 5

 $ pyfunc -m string.upper -a test
 TEST

 $ pyfunc -m string.replace -a 'analyze what' 'what' 'this'
 analyze this

6
这个脚本与其他答案类似,但它还列出了可用的函数、参数和文档字符串。
"""Small script to allow functions to be called from the command line.
Run this script without argument to list the available functions:

    $ python many_functions.py
    Available functions in many_functions.py:

    python many_functions.py a  : Do some stuff

    python many_functions.py b  : Do another stuff

    python many_functions.py c x y : Calculate x + y

    python many_functions.py d  : ?

Run this script with arguments to try to call the corresponding function:

    $ python many_functions.py a
    Function a

    $ python many_functions.py c 3 5
    3 + 5 = 8

    $ python many_functions.py z
    Function z not found
"""

import sys
import inspect

#######################################################################
#                         Your functions here                         #
#######################################################################

def a():
    """Do some stuff"""
    print("Function a")

def b():
    """Do another stuff"""
    a()
    print("Function b")

def c(x, y):
    """Calculate x + y"""
    print(f"{x} + {y} = {int(x) + int(y)}")

def d():
    # No doc
    print("Function d")

#######################################################################
#         Some logic to find and display available functions          #
#######################################################################

def _get_local_functions():
    local_functions = {}
    for name, obj in inspect.getmembers(sys.modules[__name__]):
        if inspect.isfunction(obj) and not name.startswith('_') and obj.__module__ == __name__:
            local_functions[name] = obj
    return local_functions

def _list_functions(script_name):
    print(f"Available functions in {script_name}:")
    for name, f in _get_local_functions().items():
        print()
        arguments = inspect.signature(f).parameters
        print(f"python {script_name} {name} {' '.join(arguments)} : {f.__doc__ or '?'}")


if __name__ == '__main__':
    script_name, *args = sys.argv
    if args:
        functions = _get_local_functions()
        function_name = args.pop(0)
        if function_name in functions:
            function = functions[function_name]
            function(*args)
        else:
            print(f"Function {function_name} not found")
            _list_functions(script_name)
    else:
        _list_functions(script_name)

不带参数运行此脚本可列出可用的函数:

$ python many_functions.py
Available functions in many_functions.py:

python many_functions.py a  : Do some stuff

python many_functions.py b  : Do another stuff

python many_functions.py c x y : Calculate x + y

python many_functions.py d  : ?

使用参数运行此脚本,尝试调用相应的函数:

$ python many_functions.py a
Function a

$ python many_functions.py c 3 5
3 + 5 = 8

$ python many_functions.py z
Function z not found

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接