Python - 检测我的对象写入stdout的时间?

时间:2014-06-19 18:35:34

标签: python stdout

我有一个相当不寻常的要求,我想......在我解释之后,我会解释原因。

什么

我想检测我的对象何时写入stdout,以便我可以在那时执行副作用。所以,例如,当我输入:

sys.stdout.write(instance_of_my_class)

它应该执行副作用。我已将我的班级设为str的子类,并覆盖__call____unicode____str____repr__index,{ {1}},decodeencodeformat__format____getattribute____getitem__,以便每个人都打印一个声明他们已被调用,但似乎__len__没有调用那些来打印对象。

请注意,我是专门讨论sys.stdout.write而不是sys.stdout.write - 我发现print调用了print

为什么

这个问题继续从Colored Python Prompt in Windows?的答案开始。

我发现每次python需要显示交互式提示时,它会在__str____str__上调用sys.ps1,然后保存结果以显示在命令行上。这意味着sys.ps2中的任何副作用都是在sys.ps2.__str__之后产生的,但我希望让他们等到显示sys.ps1.__str__的时间。

因此,我不是在sys.ps2中返回str,而是返回sys.ps2.__str__的子类,我希望能够以某种方式在某种程度上捕获在其上调用str

2 个答案:

答案 0 :(得分:4)

有趣的问题!我的第一个猜测是sys.stdout.write没有调用__str__方法,因为您的对象已经 一个str(或者至少是它的一个子类,这对所有意图和目的来说都足够好了......所以不需要任何铸造方法。

进一步调查显示,sys.stdout.write确实不想调用__str__方法......

子类方法

通过一点反省,您可以找到str调用sys.stdout.write子类 的方法(答案是,并不多):

class superstring(str):
    def __getattribute__(self, name):
        print "*** lookup attribute %s of %s" % (name, repr(self))
        return str.__getattribute__(self, name)

foo = superstring("UberL33tPrompt> ")
sys.stdout.write(foo)

在Unicode环境(Python 2.7,iPython notebook)中运行,打印:

*** lookup attribute __class__ of 'UberL33tPrompt> '
*** lookup attribute decode of 'UberL33tPrompt> '
UberL33tPrompt> 

看起来很像kludge-y,但你可以覆盖子类的decode方法来执行所需的副作用。

但是,在非Unicode环境中没有属性查找

Wrapper方法

不是使用str的子类,也许你需要的是某种"包装器"在str附近。这是一个丑陋的探索性黑客,它创建了一个将其大部分属性委托给str的类,但它不是严格意义上的子类:

class definitely_not_a_string(object):
    def __init__(self, s):
        self.s = s
    def __str__(self):
        print "*** Someone wants to see my underlying string object!"
        return self.s
    def decode(self, encoding, whatever):
        print "*** Someone wants to decode me!"
        return self.s.decode(encoding, whatever)
    def __getattribute__(self, name):
        print "*** lookup attribute %s of %s" % (name, repr(self))
        if name in ('s', '__init__', '__str__', 'decode', '__class__'):
            return object.__getattribute__(self, name)
        else:
            return str.__getattribute__(self, name)

foo = definitely_not_a_string("UberL33tPrompt> ")
sys.stdout.write(foo)

在Unicode环境中,这给出了基本相同的结果:

*** lookup attribute __class__ of <__main__.definitely_not_a_string object at 0x00000000072D79B0>
*** lookup attribute decode of <__main__.definitely_not_a_string object at 0x00000000072D79B0>
*** Someone wants to decode me!
*** lookup attribute s of <__main__.definitely_not_a_string object at 0x00000000072D79B0>
UberL33tPrompt> 

但是,当我在非Unicode环境中运行时,definitely_not_a_string会显示错误消息:

TypeError: expected a character buffer object

...这表明当.write方法不需要进行任何Unicode解码时,它将直接进入C级buffer interface

我的结论

似乎在Unicode环境中覆盖decode方法是可能的kludge ,因为sys.stdout.write在需要将str解码为.write时调用此方法的Unicode。

但是,在非Unicode环境中,似乎help(sys.stdout.write)没有进行任何属性查找,而只是直接进入C级字符缓冲协议,所以没有办法从Python代码拦截其访问权限。实际上,{{1}}验证它是一个内置函数(也就是用C编写,而不是Python)。

答案 1 :(得分:2)

为什么不monkeypatch stdout.write?

stdoutRegistry = set()

class A(object):
    def __init__(self):
        self.stdoutRegistry.add(self)

    def stdoutNotify(self):
        pass

original_stdoutWrite = sys.stdout.write
def stdoutWrite(*a, **kw):
    if a in stdoutRegistry:
        a.stdoutNotify()
    original_stdoutWrite(*a, **kw)
sys.stdout.write = stdoutWrite