8

我正在使用 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")

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

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

编辑:

我试图保持简单,但看起来这引起了更多的混乱......

  1. 重载 print 的函数将打印到 GUI,因此如果函数重载,则无需关心重定向。
  2. 我知道我的示例没有执行 print 函数实际执行的操作。一个更好的例子来说明我是如何考虑编码的(我知道仍然不是很好):

    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)
    
  3. 从上面的 2 中,您可以看到我必须用来打印到 GUI“hli_Print”的函数,它是一个通过 swig 包装器公开的 C++ 函数。

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

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

4

2 回答 2

15

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

print函数在模块中定义(在 Python 3__builtin__中重命名为)。builtinsPython 解释器使__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)

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

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

于 2012-12-14T02:36:04.857 回答
7

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

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

其次,您说“我需要这样做,因为此代码可能用于必须使用内置函数生成输出的系统上,否则不会显示输出。” 好吧, yourNewPrint不是内置函数,所以无论如何这都无济于事。

还值得注意的是,您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()

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

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

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

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

是的,这是一个更合理的选择。但我可能不会这样称呼它print_。并且将“做或不做”逻辑放在函数中更简单,而不是在全局名称中进出交换实现(特别是因为如果您的代码不是全部,您将在某个时候搞砸一个大模块)。

我曾在一个具有类似用例的项目中工作:我们有想要进入系统日志的消息,如果它是打开的,还可以进入一个 GUI“日志窗口”。所以我们写了一个glog函数来包装它,没有人抱怨他们想改写print。(事实上​​,团队中至少有一个人非常高兴他可以print在调试时使用快速而肮脏的打印输出而不影响实际输出,尤其是当他必须调试 GUI 日志代码时。)

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

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

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

那么为什么不首先使用 3.x 呢?显然 3.x 具有实际的 3.x 标准库,而不是类似于 3.x 标准库的东西,如果您执行一些__future__语句,并且 SWIG 与 3.x 一起使用......</p>

于 2012-12-14T00:36:57.370 回答