在 Python 中如何放置一个钩子劫持接下来发生的标准输出?

2021-08-07 02:41:16 +08:00
 LeeReamond

如题,假设在 a.py 里有一段 print 代码

# a.py
print("hello everyone, this is a.py")

假设我们在不修改 a.py 的情况下,可否在其他文件中通过劫持标准输出的方式修改这段内容的输出?

比如我们期望的使用环境是在b.py中导入a.py

# b.py
# 首先劫持标准输出
do something

# 然后导入 a.py
import a

# 期望得到定制化的输出
# 比如
# 打印 this is a.py at 2021-08-08 12:12:12 (删除前面的 hello everyone,之后再添加时间)
2344 次点击
所在节点    Python
18 条回复
toaruScar
2021-08-07 03:06:48 +08:00
只要不修改 a.py 吗?
直接在 b.py 里用文件操作把 a.py 拷贝成 c.py ,然后修改 c.py 的那个 print 的参数,然后再导入 c.py
toaruScar
2021-08-07 03:07:39 +08:00
*然后再导入 b py 那个文件
aijam
2021-08-07 03:12:13 +08:00
```
from unittest.mock import patch
_print = print
with patch('builtins.print') as mock:
mock.side_effect = lambda x: _print("whatever you want")
import a
```
LeeReamond
2021-08-07 03:49:23 +08:00
@aijam 你好,我没有理解这段代码的逻辑
下面的代码我试了输出结果是 hello world
```
from unittest.mock import patch

_print = print
with patch('builtins.print') as mock:
mock.side_effect = lambda x: _print("whatever you want")

print("hello world")
```
aijam
2021-08-07 03:57:20 +08:00
@LeeReamond print 在 with block 里面
binux
2021-08-07 04:12:35 +08:00
覆盖一个 sys.stdout ?
LeeReamond
2021-08-07 04:44:57 +08:00
@aijam 感谢,学习了,目前看来应该可以满足需求。看了看代码似乎是用魔术方法重载并替换内建方法。unitest 是内建库所以应该直接使用这个封装好的版本就可以了,不用自己再实现一遍。我也是第一次知道 python 可以跑 import builtins 这种东西
LeeReamond
2021-08-07 05:55:31 +08:00
@aijam 后来又想了一下还是不太对啊,即使用魔术方法重新引入 buildins,它是怎么做到在 b 里的代码也不应该影响到 a 的 namespace 啊。。
noobsheldon
2021-08-07 09:35:57 +08:00
ClericPy
2021-08-07 11:40:25 +08:00
之前就像 6 楼说的重新覆盖一个新 class 代替默认的 sys.stdout 就行了, 当时是为了截获标准输出的时候复制一份到本地文件里以及主动 flush
ruanimal
2021-08-07 11:43:35 +08:00
from io import StringIO
import sys

buf = StringIO()
sys.stdout = buf
print('nothing')
sys.stdout = sys.__stdout__
print('something')
NoAnyLove
2021-08-07 11:46:06 +08:00
jaredyam
2021-08-07 13:39:51 +08:00
你的目的归根结底就是把 stdout 转成 string 的 return,然后对 string 进行操作。
LeeReamond
2021-08-07 21:52:18 +08:00
@ruanimal
@ClericPy
@jaredyam 上面#11 的代码确实捕捉成功了,只是疑问是,正常来说 a.pyb.py ,如果两者都 import 同一个模块的话,应该是互相独立互不影响的,在各自范围内按照各自的逻辑工作,为什么在楼上的脚本里,通过 b 修改 sys.stdout 可以影响 a 的 stdout 的行为?
ruanimal
2021-08-07 22:08:13 +08:00
这设计到作用域
模块是全局对象,sys.stdout 是全局生效的,可以去了解下 mock 和猴子补丁
MiketsuSmasher
2021-08-08 08:39:07 +08:00
可以参考 stackoverflow 上的这个回答,整一个上下文管理器,把需要捕捉输出的代码扔进去
https://stackoverflow.com/a/6796752/16472044
wwqgtxx
2021-08-08 23:10:23 +08:00
@LeeReamond #14 “正常来说 a.pyb.py ,如果两者都 import 同一个模块的话,应该是互相独立互不影响的,在各自范围内按照各自的逻辑工作” 这句话本来就不成立,除非你在 a 中这样写:
import sys
stdout = sys.stdout
import b
这样的话,如果在 b 中修改 sys.stdout,并不会影响 a 中的 stdout,但依然会影响 sys.stdout
generated
2021-08-13 10:57:00 +08:00

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/794208

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX