提问者:小点点

在Python中设置只读属性?


鉴于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解决方案,如果这很重要的话,尽管没有理由拒绝给出适用于其他版本的答案,因为我怀疑将来其他人会在这里提出关于其他版本的类似问题。


共2个答案

匿名用户

尽管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主要是一种动态语言,但有一些本机对象类型,如strfile(包括stdout)、dictlist,它们实际上是在低级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