在Python中设置只读属性?

时间:2014-06-30 19:16:02

标签: python python-2.7 stdout readonly-attribute

考虑到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' object attribute 'write' is read-only

这是一个很好的尝试,让我不要做一些潜在的(可能)愚蠢的事情,但我真的很想继续这样做。我怀疑解释器有一些我可以修改的查找表,但我在Google上找不到类似的东西。 __setattr__也不起作用 - 它返回了与只读属性完全相同的错误。

我正在寻找一个Python 2.7解决方案,如果这很重要,虽然没有理由拒绝投入适用于其他版本的答案,因为我怀疑未来的其他人会在这里看到与其他版本类似的问题。

2 个答案:

答案 0 :(得分:20)

尽管它具有动态性,但它不允许使用猴子修补内置类型,例如file。它甚至可以通过修改此类型的__dict__来阻止您这样做 - __dict__属性返回包含在只读代理中的dict,因此分配给file.write和{ {1}}失败。并且至少有两个很好的理由:

  1. C代码要求file.__dict__['write']内置类型与file类型结构相对应,PyFile与内部使用的file.write函数相对应。 / p>

  2. Python实现了对类型的属性访问的缓存,以加速方法查找和实例方法创建。如果允许直接分配到类型dicts,则该缓存将被破坏。

  3. 当然,允许使用Python实现的类补丁,它可以很好地处理动态修改。

    然而......如果您真的知道自己在做什么,可以使用PyFile_Write()之类的低级API来挂钩实现并转到类型dict。例如:

    ctypes

答案 1 :(得分:3)

尽管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