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 · 81 天前 · 1385 次点击
    这是一个创建于 81 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题,假设在 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   81 天前
    只要不修改 a.py 吗?
    直接在 b.py 里用文件操作把 a.py 拷贝成 c.py ,然后修改 c.py 的那个 print 的参数,然后再导入 c.py
    toaruScar
        2
    toaruScar   81 天前
    *然后再导入 b py 那个文件
    aijam
        3
    aijam   81 天前   ❤️ 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   81 天前
    @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   81 天前
    @LeeReamond print 在 with block 里面
    binux
        6
    binux   81 天前 via Android
    覆盖一个 sys.stdout ?
    LeeReamond
        7
    LeeReamond   81 天前
    @aijam 感谢,学习了,目前看来应该可以满足需求。看了看代码似乎是用魔术方法重载并替换内建方法。unitest 是内建库所以应该直接使用这个封装好的版本就可以了,不用自己再实现一遍。我也是第一次知道 python 可以跑 import builtins 这种东西
    LeeReamond
        8
    LeeReamond   81 天前 via Android
    @aijam 后来又想了一下还是不太对啊,即使用魔术方法重新引入 buildins,它是怎么做到在 b 里的代码也不应该影响到 a 的 namespace 啊。。
    ClericPy
        10
    ClericPy   81 天前
    之前就像 6 楼说的重新覆盖一个新 class 代替默认的 sys.stdout 就行了, 当时是为了截获标准输出的时候复制一份到本地文件里以及主动 flush
    ruanimal
        11
    ruanimal   81 天前
    from io import StringIO
    import sys

    buf = StringIO()
    sys.stdout = buf
    print('nothing')
    sys.stdout = sys.__stdout__
    print('something')
    jaredyam
        13
    jaredyam   81 天前
    你的目的归根结底就是把 stdout 转成 string 的 return,然后对 string 进行操作。
    LeeReamond
        14
    LeeReamond   81 天前
    @ruanimal
    @ClericPy
    @jaredyam 上面#11 的代码确实捕捉成功了,只是疑问是,正常来说 a.pyb.py ,如果两者都 import 同一个模块的话,应该是互相独立互不影响的,在各自范围内按照各自的逻辑工作,为什么在楼上的脚本里,通过 b 修改 sys.stdout 可以影响 a 的 stdout 的行为?
    ruanimal
        15
    ruanimal   81 天前
    这设计到作用域
    模块是全局对象,sys.stdout 是全局生效的,可以去了解下 mock 和猴子补丁
    MiketsuSmasher
        16
    MiketsuSmasher   80 天前
    可以参考 stackoverflow 上的这个回答,整一个上下文管理器,把需要捕捉输出的代码扔进去
    https://stackoverflow.com/a/6796752/16472044
    wwqgtxx
        17
    wwqgtxx   80 天前
    @LeeReamond #14 “正常来说 a.pyb.py ,如果两者都 import 同一个模块的话,应该是互相独立互不影响的,在各自范围内按照各自的逻辑工作” 这句话本来就不成立,除非你在 a 中这样写:
    import sys
    stdout = sys.stdout
    import b
    这样的话,如果在 b 中修改 sys.stdout,并不会影响 a 中的 stdout,但依然会影响 sys.stdout
    generated
        18
    generated   75 天前 via Android
    关于   ·   帮助文档   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2130 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 16:12 · PVG 00:12 · LAX 09:12 · JFK 12:12
    ♥ Do have faith in what you're doing.