如何"超载"python的"全局"打印功能?

Mac*_*Mac 8 python namespaces python-2.6

我使用python 2.6.6,我需要重载默认的python打印功能.我需要这样做,因为此代码可用于必须使用内置函数生成输出的系统,否则不显示输出.

所以,例如,如果你有一个像这样的python脚本:

from __future__ import print_function

def NewPrint(Str):
    with open("somefile.txt","a") as AFile:
        AFile.write(Str)

def OverloadPrint():
    global print
    print = NewPrint

OverloadPrint()
print("ha")
Run Code Online (Sandbox Code Playgroud)

它工作正常."重载"打印的输入位于NewPrint指定的文件中.

现在考虑到这一点,我希望能够在上面的几行中运行并打印以执行NewPrint在整个脚本执行期间所做的事情.现在,如果我从另一个使用print的模块调用一个函数,它将使用内置的print而不是我刚刚覆盖的那个.我想这与命名空间和内置函数有关,但我的python不够好.

编辑:

我试着保持简单,但看起来这样会造成更多的混乱......

  1. 重载打印的功能将打印到GUI,因此如果函数过载,则无需关心重定向.
  2. 我知道我的例子没有做打印功能实际做的事情.我如何考虑编码它的一个更好的例子是(我知道仍然不是很好):

    def hli_print(*args, **kw):
        """
        print([object, ...], sep=' ', end='\n', file=sys.stdout)
        """
        sep = kw.get('sep', ' ')
        end = kw.get('end', '\n')
        File = kw.get('file', sys.stdout)
        args = [str(arg) for arg in args]
        string = sep.join(args) + end
        File.write(string)
        hli_Print(string)
    
    Run Code Online (Sandbox Code Playgroud)
  3. 从上面的2开始,您可以看到我必须使用的功能打印到GUI"hli_Print"它是一个通过swig包装器公开的C++函数.

  4. 我们只使用标准库和我们自己的swig包装器,我们的开发人员也使用print作为函数(习惯于3.X).所以我并没有真正担心其他一些模块调用print而不是其他的东西.

从所有的评论我想只是使用一些print_()函数而不是print()(这是我们目前做的)可能是最好的,但我真的很好奇,看看在python中是否有可能做我做的事情描述.

Blc*_*ght 13

正如@ abarnert的回答和几条评论指出的那样,替换print可能不是一个好主意.但只是为了完整起见,这就是为什么你的代码没有成功覆盖其他模块的原因.

print函数在模块中定义__builtin__(builtins在Python 3中重命名).Python解释器使__builtin__模块命名空间中的所有内容都可用于运行的所有其他代码,而无需将其导入模块自己的命名空间.这太神奇了!

但是,当您创建自己的名为print(使用print = NewPrint)的函数时,它不会覆盖原始版本__builtin__.您只需在模块的命名空间中创建一个新变量,该变量将隐藏旧变量__builtin__.该global语句没有帮助,因为它只允许您告诉Python您要写入模块的全局命名空间而不是函数内部的一些内部命名空间.

要替换默认print功能,您需要在__builtin__模块中明确替换它.这是一些示例代码:

from __future__ import print_function
try:
    import __builtin__ as builtins # Python 2
except ImportError:
    import builtins # Python 3

_print = print # keep a local copy of the original print
builtins.print = lambda *args, **kwargs: _print("foo:", *args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

重复一遍,这真的不是一个好主意.在确保我理解我在这个答案中所讨论的内容的同时,我设法通过替换print为不接受filePython用于打印到标准错误的参数的lambda函数来使我的一个Python会话崩溃.几行后,异常处理程序在尝试打印另一个异常的回溯时遇到第二个异常时并不高兴.

几乎可以肯定有更好的方法来获得你想要的结果.


aba*_*ert 7

我认为你的问题没有任何意义.

首先,如果您正在运行Python 2.6,那么您导入的所有内容等都将使用print语句,即使您自己的模块正在使用该print函数.因此,重载函数不会影响其他任何事情.

其次,你说"我需要做的,因为此代码的系统,其中内置的功能已被用来生成输出上使用,否则没有任何输出." 好吧,你NewPrint不是一个内置功能,所以这无论如何都无济于事.

另外值得一提的是,你NewPrint不执行大部分的功能print作用,甚至认为其位落实,它错误(print(s)将打印s 跟着一个换行符).所以,如果你没有更换内置print有你的功能,你只是最终破坏最自己的代码和任何你依赖STDLIB /第三方代码.

您可以通过创建替换的类文件对象来完成您想要的任务sys.stdout.否则,我看不出有什么可行的.例如:

class FakeStdOut(object):
    # … lots of other stuff to implement or inherit
    def write(s):
        with open("somefile.txt", "a") as f:
            f.write(s)

def OverloadPrint():
    sys.stdout = FakeStdOut()
Run Code Online (Sandbox Code Playgroud)

但即使这样可行,也可能不是你真正想要的.对于快速而肮脏的脚本,在具有缺陷外壳的平台上,这有时是一个方便的想法.但除此之外,从长远来看,它可能会给你带来更多麻烦,而不是提出更好的解决方案.这里只是一些可能出错的事情(仅作为示例,而不是详尽的列表)

  • 如果您想要更改输出的文件,则必须修改脚本.如果你改为>>在shell中使用,你可以用不同的方式调用脚本.
  • 有人阅读或调试你的代码(例如,你,在你忘记它的工作方式三个月之后)会对正在发生的事情感到惊讶.
  • 一些stdlib /第三方/同事/等.您调用的代码将stdout在您进行更改之前检查是否为tty,并将其自身配置为交互式输出.
  • 在您有机会重定向之前会打印一些代码,并且您将花费数小时试图找出如何重新排序以解决问题的方法.
  • 您必须知道如何完全实现"类文件对象" - 并且该概念未在2.6中完全定义 - 否则它将破坏某些代码.
  • 在某个地方,有一些你认为正在编写的代码print,但它实际上是,例如,logging或写入sys.stderr或做其他事情,所以你给自己一种虚假的安全感,你现在正在记录所有内容somefile.txt,并赢得了'然后发现,直到6个月之后,当您迫切需要缺少信息来调试客户站点的问题时.

既然你已经编辑了这个问题,这里有一些进一步的回答:

从所有的评论我想只是使用一些print_()函数而不是print()

是的,这是一个更合理的选择.但我可能不会这么称呼它print_.将"执行或不执行"逻辑放在函数内部更简单,而不是将实现交换到全局名称中(特别是因为如果您的代码不是全部,那么您将在某些时候将其搞砸了)一个大模块).

我参与了一个类似用例的项目:我们想要发送到syslogs的消息,如果它是打开的话,还会进入GUI"日志窗口".所以我们编写了一个glog包含该功能的函数,没有人抱怨他们想要编写代码print.(事实上​​,团队中至少有一个人非常高兴他可以print在调试时使用快速打印输出,而不会影响实际输出,特别是当他必须调试GUI日志代码时.)

但那仅仅是因为我们对新的(当时的)logging模块没有任何经验.如今,我想我会创建一个logging Handler写入GUI窗口的实现,只需添加该处理程序,并在logging各处使用标准方法.听起来这可能是你的最佳选择.

此外,最后一个可能无关的问题是:

我们只使用标准库和我们自己的swig包装器,我们的开发人员也使用print作为函数(习惯于3.X).

那么为什么不首先使用3.x?显然3.X有实际3.x的标准库,而不是什么样的接近,如果你做一些3.x的标准库__future__语句,并痛饮可与3.X ...