V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
LeeReamond
V2EX  ›  Python

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

  •  
  •   LeeReamond · 2021-08-07 02:41:16 +08:00 · 2316 次点击
    这是一个创建于 965 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题,假设在 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,之后再添加时间)
    
    18 条回复    2021-08-13 10:57:00 +08:00
    toaruScar
        1
    toaruScar  
       2021-08-07 03:06:48 +08:00
    只要不修改 a.py 吗?
    直接在 b.py 里用文件操作把 a.py 拷贝成 c.py ,然后修改 c.py 的那个 print 的参数,然后再导入 c.py
    toaruScar
        2
    toaruScar  
       2021-08-07 03:07:39 +08:00
    *然后再导入 b py 那个文件
    aijam
        3
    aijam  
       2021-08-07 03:12:13 +08:00   ❤️ 1
    ```
    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
        4
    LeeReamond  
    OP
       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
        5
    aijam  
       2021-08-07 03:57:20 +08:00
    @LeeReamond print 在 with block 里面
    binux
        6
    binux  
       2021-08-07 04:12:35 +08:00 via Android
    覆盖一个 sys.stdout ?
    LeeReamond
        7
    LeeReamond  
    OP
       2021-08-07 04:44:57 +08:00
    @aijam 感谢,学习了,目前看来应该可以满足需求。看了看代码似乎是用魔术方法重载并替换内建方法。unitest 是内建库所以应该直接使用这个封装好的版本就可以了,不用自己再实现一遍。我也是第一次知道 python 可以跑 import builtins 这种东西
    LeeReamond
        8
    LeeReamond  
    OP
       2021-08-07 05:55:31 +08:00 via Android
    @aijam 后来又想了一下还是不太对啊,即使用魔术方法重新引入 buildins,它是怎么做到在 b 里的代码也不应该影响到 a 的 namespace 啊。。
    ClericPy
        10
    ClericPy  
       2021-08-07 11:40:25 +08:00
    之前就像 6 楼说的重新覆盖一个新 class 代替默认的 sys.stdout 就行了, 当时是为了截获标准输出的时候复制一份到本地文件里以及主动 flush
    ruanimal
        11
    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')
    jaredyam
        13
    jaredyam  
       2021-08-07 13:39:51 +08:00
    你的目的归根结底就是把 stdout 转成 string 的 return,然后对 string 进行操作。
    LeeReamond
        14
    LeeReamond  
    OP
       2021-08-07 21:52:18 +08:00
    @ruanimal
    @ClericPy
    @jaredyam 上面#11 的代码确实捕捉成功了,只是疑问是,正常来说 a.pyb.py ,如果两者都 import 同一个模块的话,应该是互相独立互不影响的,在各自范围内按照各自的逻辑工作,为什么在楼上的脚本里,通过 b 修改 sys.stdout 可以影响 a 的 stdout 的行为?
    ruanimal
        15
    ruanimal  
       2021-08-07 22:08:13 +08:00
    这设计到作用域
    模块是全局对象,sys.stdout 是全局生效的,可以去了解下 mock 和猴子补丁
    MiketsuSmasher
        16
    MiketsuSmasher  
       2021-08-08 08:39:07 +08:00
    可以参考 stackoverflow 上的这个回答,整一个上下文管理器,把需要捕捉输出的代码扔进去
    https://stackoverflow.com/a/6796752/16472044
    wwqgtxx
        17
    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
        18
    generated  
       2021-08-13 10:57:00 +08:00 via Android
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2763 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 11:56 · PVG 19:56 · LAX 04:56 · JFK 07:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.