鉴于Python的动态性,如果这是不可能的,我会感到震惊:
我想更改sys.stdout.write
的实现。
我从对我另一个问题的回答中得到了这个想法:https://stackoverflow.com/a/24492990/901641
我试着简单地写下:
original_stdoutWrite = sys.stdout.write
def new_stdoutWrite(*a, **kw):
original_stdoutWrite("The new one was called! ")
original_stdoutWrite(*a, **kw)
sys.stdout.write = new_stdoutWrite
但它告诉我AttributeError:'file'对象属性'write'是只读的
。
这是一个很好的尝试来阻止我做一些潜在的(可能的)愚蠢的事情,但是不管怎样,我真的很想继续做下去。我怀疑解释器有某种我可以修改的查找表,但是我在谷歌上找不到类似的东西。__setattr__
也不起作用——它返回了完全相同的关于属性为只读的错误。
我特别想要一个Python 2.7解决方案,如果这很重要的话,尽管没有理由拒绝给出适用于其他版本的答案,因为我怀疑将来其他人会在这里提出关于其他版本的类似问题。
尽管Python具有动态性,但它不允许对内置类型(如file
)进行猴子补丁。它甚至可以通过修改此类类型的\uuuuuu dict\uuuu
来阻止您这样做,\uuuuuuu dict\uuuu
属性返回封装在只读代理中的dict,因此这两个赋值都将被分配到文件。写入
和
文件__dict_uu['write']
失败。至少有两个理由:
>
C代码期望文件
内置类型对应于PyFile
类型结构和文件。将
写入内部使用的PyFile\u write()
函数。
Python在类型上实现属性访问缓存,以加快方法查找和实例方法创建。如果允许直接将此缓存分配给类型dicts,则此缓存将被破坏。
当然,在Python中实现的类可以很好地处理动态修改,因此允许使用猴子补丁。
然而如果您真的知道自己在做什么,您可以使用低级API,例如ctypes
,来连接到实现中并获得类型dict。例如:
# WARNING: do NOT attempt this in production code!
import ctypes
def magic_get_dict(o):
# find address of dict whose offset is stored in the type
dict_addr = id(o) + type(o).__dictoffset__
# retrieve the dict object itself
dict_ptr = ctypes.cast(dict_addr, ctypes.POINTER(ctypes.py_object))
return dict_ptr.contents.value
def magic_flush_mro_cache():
ctypes.PyDLL(None).PyType_Modified(ctypes.py_object(object))
# monkey-patch file.write
dct = magic_get_dict(file)
dct['write'] = lambda f, s, orig_write=file.write: orig_write(f, '42')
# flush the method cache for the monkey-patch to take effect
magic_flush_mro_cache()
# magic!
import sys
sys.stdout.write('hello world\n')
尽管Python主要是一种动态语言,但有一些本机对象类型,如str
、file
(包括stdout
)、dict
和list
,它们实际上是在低级C中实现的完全静态的:
>>> a = []
>>> a.append = 'something else'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object attribute 'append' is read-only
>>> a.hello = 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'hello'
>>> a.__dict__ # normal python classes would have this
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__dict__'
如果您的对象是本机C代码,您唯一的希望就是使用一个实际的正则类。对于你的情况,就像前面提到的,你可以做这样的事情:
class NewOut(type(sys.stdout)):
def write(self, *args, **kwargs):
super(NewOut, self).write('The new one was called! ')
super(NewOut, self).write(*args, **kwargs)
sys.stdout = NewOut()
或者,执行与原始代码类似的操作:
original_stdoutWrite = sys.stdout.write
class MyClass(object):
pass
sys.stdout = MyClass()
def new_stdoutWrite(*a, **kw):
original_stdoutWrite("The new one was called! ")
original_stdoutWrite(*a, **kw)
sys.stdout.write = new_stdoutWrite