stdin 重定向对 python 解释器做了什么

What is a stdin redirect doing to the python interpreter

我有以下情况,我不明白也许你可以指出答案或向我解释一下。

我有以下python文件结构结构:

project/ -folder_a/ -File_a -folder_b/ -File_b

File_a 正在导入 File_b。 File_a 是主文件,但如果我这样调用它,我只能从项目文件夹中 运行 它。

python < folder_a/File_a

否则我会收到无法导入 File_b 的导入错误。我知道“<”符号是标准输入的重定向,但它对 python 解释器做了什么,为什么它只能以这种方式工作。

非常感谢, 制作

Python 可以 运行 编码几种不同的方式:你可以给它一个 运行 的脚本,或者 -m 的模块,或者 -m 的命令=16=]。但是如果你不给它任何一个,它会读取标准输入并一次执行一条语句直到 EOF。

您已经习惯了使用交互式解释器看到这一点:

$ python
Python 2.7.10 (default, Oct  6 2017, 22:29:07)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> print('hello')
hello
>>> ^D
$

它从标准输入读取 print('hello') 并执行它。然后它将 ctrl-D 读取为 EOF 并退出。

如果标准输入不是交互式控制台(实际上,if not sys.stdin.isatty():),它不会打印横幅,显示 >>> 提示,启用 readline 命令行editing等,但还是一条一条的读取执行,直到EOF。

当您执行 python < something.py 时,您的 shell 正在将文件 something.py 传送到 Python 的标准输入中。由于该文件不是交互式控制台,因此 Python 不会执行所有交互式操作;它只是从脚本中读取和执行语句。


这与 运行ning python something.py 相似,但不完全相同。

最大的区别是 Python 不知道你给它什么脚本;它只能看到文件的 contents,看不到文件名或其他任何东西,它甚至无法判断它们来自文件而不是例如从另一个文件通过管道传输程序。

如果您查看 sys.path 的工作原理:

As initialized upon program startup, the first item of this list, path[0], is the directory containing the script that was used to invoke the Python interpreter. If the script directory is not available (e.g. if the interpreter is invoked interactively or if the script is read from standard input), path[0] is the empty string, which directs Python to search modules in the current directory first.

所以,实际上,python folder_a/File_a.py./folder_a 放在 sys.path 上,而 python < folder_a/File_a.py. 放在 sys.path 上。

这确实不是解决您问题的好方法,但它并不能解释为什么大多数情况下都有效。


一个更好的解决方案是重新组织您的代码,以便您拥有充满要导入的模块的包,然后是您想要 运行 在这些包之外的任何顶级脚本。像这样:

project/
 script.py
 -pkg_a/
   -__init__.py
   -module_a.py
 -pkg_b/
   -__init__.py
   -module_b.py

那些 __init__.py 文件在 Python 3 中实际上不是必需的,但是它们向 Python 解释器和您的 reader 发出信号是 "ordinary packages"(与命名空间包或根本不是包的目录相反)。

现在,script.py 可以 import 和 运行 来自 module_a.py 的代码与任何其他 Python 代码相同。例如,而不是这个:

# pkg_a/module_a.py
print('hello')

... 这样做:

# pkg_a/module_a.py
def run():
    print('hello')

# script.py
from pkg_a.module_a import run
run()

如果您打算使用 setuptools 使您的代码可通过 pip 安装,您可以走得更远——将 pkg_a.module_a.run 指定为 "entry point",并且 pip 将为您创建 script.py,确保它可执行,为用户特定 Python 设置 shbang 行,将其安装在用户路径的某处,等等


如果您的设计中存在某些问题使得无法或不适合将 "script" 代码移出您的模块并移至单独的脚本中,您始终可以 运行 将其作为一个模块,与你在 stdlib 中做的一样:

$ python -m pip install spam
<installs the spam package>
$ echo '[{"dense":"json"}]' | python -m json.tool
[
    {
        "dense": "json"
    }
]
$ python -m pkg_a.module_a
<runs the code in pkg_a/module_a.py as a module>