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

时间:2018-08-13 17:41:01

标签: python shell redirect import stdin

我有以下情况,我不了解,也许您可​​以指出答案或向我解释。

我具有以下python文件结构:

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

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

python < folder_a/File_a

否则,我将收到无法导入File_b的导入错误。我知道“ <”符号是stdin的重新定义,但是它对python解释器有什么作用,为什么它只能以这种方式工作。

非常感谢, 化妆

1 个答案:

答案 0 :(得分:1)

Python可以通过几种不同的方式运行代码:您可以给它提供运行脚本,使用-m的模块或使用-c的命令。但是,如果您不输入任何内容,它将读取标准输入并一次执行一条语句,直到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命令行编辑,等等。但是它仍会一一读取并执行语句,直到EOF。

执行python < something.py时,您的外壳程序将文件something.py输送到Python的标准输入中。由于该文件不是交互式控制台,因此Python不会完成所有交互式工作。它只是从脚本中读取并执行语句。


这类似于运行python something.py,但不相同。

最大的区别是Python不知道您要给它什么脚本。它只能看到文件的内容,而不能看到文件名或其他任何内容,它甚至不能说出它们是来自文件,而不是例如来自另一个程序的管道。 / p>

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

  

在程序启动时初始化,此列表的第一项path[0]是包含用于调用Python解释器的脚本的目录。如果脚本目录不可用(例如,如果解释器是交互式调用的,或者从标准输入中读取了脚本),则path[0]是空字符串,它会引导Python首先在当前目录中搜索模块。

因此,实际上,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解释器和您的读者)都发出信号,它们是“普通包”(与名称空间包或目录相对)根本不是软件包。

现在,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指定为“入口点”,并指定pip将为您创建script.py,确保其可执行,为用户的特定Python设置shbang行,将其安装在用户路径上的某个位置,等等。


如果设计方面的某些事情使将“脚本”代码从模块中移出并移到单独的脚本中是不可能的或不适当的,则始终可以将其作为模块运行,就像处理模块中的代码一样。 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>