在终端执⾏Python代码的6种⽅法,原来还能这样!
来⾃ | Python猫 编译 | 豌⾖花下猫
原作 | BRETT CANNON
为了我们推出的 VS Code 的 Python 插件[1],我写了⼀个简单的脚本来⽣成变更⽇志[2](类似于Towncrier[3],但简单些,⽀持Markdown,符合我们的需求)。在发布过程中,有⼀个步骤是运⾏pyt
hon news,它会将 Python 指向我们代码中的"news"⽬录。
前⼏天,⼀位合作者问这是如何⼯作的,似乎我们团队中的每个⼈都知道如何使⽤-m?请参阅我的有关带 -m 使⽤ pip 的⽂章[4],了解原因。
这使我意识到其他⼈可能不知道有五花⼋门的⽅法可以将 Python 指向要执⾏的代码,因此有了这篇⽂章。
1、通过标准输⼊和管道
因为如何⽤管道传东西给⼀个进程是属于 shell 的内容,我不打算深⼊解释。⽏庸置疑,你可以将代码传递到 Python 中。
# 管道传内容给 python
echo "print('hi')" | python
如果将⽂件重定向到 Python,这显然也可以。
# 重定向⼀个⽂件给 python
python < spam.py
归功于 Python 的 UNIX 传统,这些都不太令⼈感到意外。
2、通过 -c 指定的字符串
如果你只需要快速地检查某些内容,则可以在命令⾏中将代码作为字符串传递。
# 使⽤ python 的 -c 参数
python -c "print('hi')"
当需要检查仅⼀⾏或两⾏代码时,我个⼈会使⽤它,⽽不是启动 REPL(译注:Read Eval Print Loop,即交互式解释器,例如在windows 控制台中输⼊python, 就会进⼊交互式解释器。-c 参数⽤法可以省去进⼊解释器界⾯的过程)
3、⽂件的路径
最众所周知的传代码给 python 的⽅法很可能是通过⽂件路径。
# 指定 python 的⽂件路径
python spam.py
要实现这⼀点的关键是将包含该⽂件的⽬录放到sys.path⾥。这样你的所有导⼊都可以继续使⽤。但这也是为什么你不能/不应该传⼊包含在⼀个包⾥的模块路径。因为sys.path可能不包含该包的⽬录,因此所有的导⼊将相对于与你预期的包不同的⽬录。
4、对包使⽤ -m
执⾏ Python 包的正确⽅法是使⽤ -m 并指定要运⾏的包名。
python -m spam
它在底层使⽤了runpy[5]。要在你的项⽬中做到这点,只需要在包⾥指定⼀个__main__.py⽂件,它将被当成__main__执⾏。⽽且⼦模块可以像任何其它模块⼀样导⼊,因此你可以对其进⾏各种测试。
我知道有些⼈喜欢在⼀个包⾥写⼀个main⼦模块,然后将其__main__.py写成:
from . import main
python怎么读取py文件if __name__ == "__main__":
main.main()
就我个⼈⽽⾔,我不感冒于单独的main模块,⽽是直接将所有相关的代码放⼊__main__.py,因为我感觉这些模块名是多余的。
(译注:即作者不关⼼作为⼊⼝⽂件的"main"或者“__main__”模块,因为执⾏时只需⽤它们的包名即可。我认为这也暗⽰了⼊⼝模块不该再被其它模块 import。)
5、⽬录
定义__main__.py也可以扩展到⽬录。如果你看⼀下促成此博客⽂章的⽰例,python news可执⾏,就是因为 news ⽬录有⼀个
__main__.py ⽂件。该⽬录就像⼀个⽂件路径被 Python 执⾏了。
现在你可能会问:“为什么不直接指定⽂件路径呢?”好吧,坦⽩说,关于⽂件路径,有件事得说清楚。在发布过程中,我可以简单地写上说明,让运⾏python news/announce.py,但是并没有确切的理由说
明这种机制何时存在。
再加上我以后可以更改⽂件名,⽽且没⼈会注意到。再加上我知道代码会带有辅助⽂件,因此将其放在⽬录中⽽不是单独作为单个⽂件是有意义的。
当然,我也可以将它变为⼀个使⽤ -m 的包,但是没必要,因为 announce 脚本很简单,我知道它要保持成为⼀个单独的⾃⾜的⽂件(少于 200 ⾏,并且测试模块也⼤约是相同的长度)
况且,__main__.py⽂件⾮常简单。
import runpy
# Change 'announce' to whatever module you want to run.
runpy.run_module('announce', run_name='__main__', alter_sys=True)
现在显然必须要处理依赖关系,但是如果你的脚本仅使⽤标准库或将依赖模块放在__main__.py旁边(译注:即同级⽬录),那么就⾜够
了!
(译注:我觉得作者在此有点“炫技”了,因为这种写法的前提是得知道 runpy 的⽤法,但是就像前⼀条所写的⽤ -m 参数运⾏⼀个包,
在底层也是⽤了 runpy。不过炫技的好处也⾮常明显,即__main__.py ⾥不⽤导⼊ announce 模块,还是以它为主模块执⾏,也就不会破
坏原来的依赖导⼊关系)
6、执⾏⼀个压缩⽂件
如果你确实有多个⽂件和/或依赖模块,并且希望将所有代码作为⼀个单元发布,你可以⽤⼀个__main__.py,放置在⼀个压缩⽂件中,并把
压缩⽂件所在⽬录放在 sys.path ⾥,Python 会替你运⾏__main__.py⽂件。
# 将⼀个压缩包传给 Python
python app.pyz
⼈们现在习惯上⽤ .pyz ⽂件扩展名来命名此类压缩⽂件,但这纯粹是传统,不会影响任何东西;你当然也可以⽤ .zip ⽂件扩展名。
为了简化创建此类可执⾏的压缩⽂件,标准库提供了zipapp[7]模块。它会为你⽣成__main__.py并添加⼀条组织⾏(shebang line),因
此你甚⾄不需要指定 python,如果你不想在 UNIX 上指定它的话。如果你想移动⼀堆纯 Python 代码,这是⼀种不错的⽅法。
不幸的是,仅当压缩⽂件包含的所有代码都是纯 Python 时,才能这样运⾏压缩⽂件。执⾏压缩⽂件对扩展模块⽆效(这就是为什么
setuptools 有⼀个 zip_safe[8]标志的原因)。(译注:扩展模块 extension module,即 C/C++ 之类的⾮ Python ⽂件)
要加载扩展模块,Python 必须调⽤ dlopen()[9]函数,它要传⼊⼀个⽂件路径,但当该⽂件路径就包含在压缩⽂件内时,这显然不起作
⽤。
我知道⾄少有⼀个⼈与 glibc 团队交谈过,关于⽀持将内存缓冲区传⼊压缩⽂件,以便 Python 可以将扩展模块读⼊内存,并将其传给压缩
⽂件,但是如果内存为此服务,glibc 团队并不同意。
但是,并⾮所有希望都丧失了!你可以使⽤诸如shiv[10]之类的项⽬,它会捆绑(bundle)你的代码,然后提供⼀个__main__.py来处理压
缩⽂件的提取、缓存,然后为你执⾏代码。尽管不如纯 Python 解决⽅案理想,但它确实可⾏,并且在这种情况下算得上是优雅的。
参考链接
原⽂链接:
精 彩 ⽂ 章
END
最后说个题外话,相信⼤家都知道视频号了,随着灰度范围扩⼤,越来越多的⼩伙伴都开通了视频号。⼩詹也开通了⼀个视频号,会分享互联⽹那些事、读书⼼得与副
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论