如何“超载”python打印功能“全局”?

时间:2012-12-14 00:29:23

标签: 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")

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

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

修改

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

  1. 重载打印的功能将打印到GUI,因此无需关心重定向 IF 该功能是否过载。
  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)
    
  3. 从上面的2开始,您可以看到我必须使用的功能来打印到GUI“hli_Print”,这是一个通过swig包装器公开的C ++函数。

  4. 我们只使用标准库和我们自己的swig包装器,我们的开发人员也使用print作为函数(习惯于3.X)。所以我并没有真正担心其他一些模块调用打印并改为使用其他模块。
  5. 从所有的评论我想只是使用一些print_()函数而不是print()(这是我们目前做的)可能是最好的,但我真的很好奇,看看在python中是否可以做我所描述的。

2 个答案:

答案 0 :(得分:12)

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

print函数在模块__builtin__中定义(在Python 3中重命名为builtins)。 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)

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

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

答案 1 :(得分: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()

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

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

由于您已编辑了问题,因此以下是一些进一步的回复:

  

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

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

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

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

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

  

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

那么为什么不首先使用3.x?显然3.x有实际的3.x标准库,如果你做一些__future__语句,而不是某种接近3.x标准库的东西,而且SWIG与3.x一起工作......