社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

涨见识了,在终端执行 Python 代码的 6 种方式

Python编程时光 • 5 年前 • 522 次点击  
点击上方“Python编程时光”,选择“加为星标
第一时间关注Python技术干货!

后台回复关键字 “黑魔法”,即可获取明哥整理的《Python黑魔法指南

原作:BRETT CANNON
译者:豌豆花下猫@Python猫
英文:https://snarky.ca/the-many-ways-to-pass-code-to-python-from-the-terminal

为了我们推出的 VS Code 的 Python 插件[1],我写了一个简单的脚本来生成变更日志[2](类似于Towncrier[3],但简单些,支持 Markdown,符合我们的需求)。在发布过程中,有一个步骤是运行python news,它会将 Python 指向我们代码中的"news"目录。

前几天,一位合作者问这是如何工作的,似乎我们团队中的每个人都知道如何使用-m?请参阅我的有关带 -m 使用 pip 的文章[4],了解原因。

这使我意识到其他人可能不知道有五花八门的方法可以将 Python 指向要执行的代码,因此有了这篇文章。

1、通过标准输入和管道

因为如何用管道传东西给一个进程是属于 shell 的内容,我不打算深入解释。毋庸置疑,你可以将代码传递到 Python 中。

# 管道传内容给 python
echo "print('hi')" | python

如果将文件重定向到 Python,这显然也可以。

# 重定向一个文件给 python
python 

归功于 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

if __name__ == "__main__":
    main.main()

就我个人而言,我不感冒于单独的main模块,而是直接将所有相关的代码放入__main__.py,因为我感觉这些模块名是多余的。

(译注:即作者不关心作为入口文件的"main"或者“main”模块,因为执行时只需用它们的包名即可。我认为这也暗示了入口模块不该再被其它模块 import。我上篇文章 [6]比作者的观点激进,认为连那句 if 语句都不该写。)

5、目录

定义__main__.py也可以扩展到目录。如果你看一下促成此博客文章的示例,python news可执行,就是因为 news 目录有一个 __main__.py文件。该目录就像一个文件路径被 Python 执行了。

img

现在你可能会问:“为什么不直接指定文件路径呢?”好吧,坦白说,关于文件路径,有件事得说清楚。😄在发布过程中,我可以简单地写上说明,让运行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 解决方案理想,但它确实可行,并且在这种情况下算得上是优雅的。

参考链接

[0] https://snarky.ca/the-many-ways-to-pass-code-to-python-from-the-terminal/

[1] https://marketplace.visualstudio.com/items?itemName=ms-python.python

[2] https://github.com/microsoft/vscode-python/tree/master/news

[3] https://pypi.org/project/towncrier

[4] https://snarky.ca/why-you-should-use-python-m-pip

[5] https://docs.python.org/3/library/runpy.html#module-runpy

[6] https://mp.weixin.qq.com/s/1ehySR5NH2v1U8WIlXflEQ

[7] https://docs.python.org/3/library/zipapp.html#module-zipapp

[8] https://setuptools.readthedocs.io/en/latest/setuptools.html#setting-the-zip-safe-flag

[9] https://linux.die.net/man/3/dlopen

[10] https://pypi.org/project/shiv



另外对文章有任何疑问,欢迎扫描下面二维码,加我微信进行交流。






推荐阅读




太赞了!《Python 黑魔法指南》终于面世了

Github上这些可视化面板也太好看了吧

5年 Python 功力,总结了 10 个开发技巧

扒开TCP的外衣,我看清了TCP的本质

如何在1s内创建上百G的超大文件?

网络知识扫盲:一篇文章搞懂 DNS




长按下图  ➡   关注博主

(按左边关注 Python, 按右边关注 Goalng





      



 


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/70079
 
522 次点击