我有一个相当不寻常的要求,我想......在我解释之后,我会解释原因。
什么
我想检测我的对象何时写入stdout,以便我可以在那时执行副作用。所以,例如,当我输入:
sys.stdout.write(instance_of_my_class)
它应该执行副作用。我已将我的班级设为str
的子类,并覆盖__call__
,__unicode__
,__str__
,__repr__
,index
,{ {1}},decode
,encode
,format
,__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
。
答案 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环境中没有属性查找。
不是使用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