简体   繁体   English

在Python中设置只读属性?

[英]Set a Read-Only Attribute in Python?

Given how dynamic Python is, I'll be shocked if this isn't somehow possible: 考虑到Python的动态性,如果不可能的话,我会感到震惊:

I would like to change the implementation of sys.stdout.write . 我想改变sys.stdout.write的实现。

I got the idea from this answer to another question of mine: https://stackoverflow.com/a/24492990/901641 我从这个答案得到了我的另一个问题的想法: https//stackoverflow.com/a/24492990/901641

I tried to simply write this: 我试着写这个:

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

But it tells me AttributeError: 'file' object attribute 'write' is read-only . 但它告诉我AttributeError: 'file' object attribute 'write' is read-only

This is a nice attempt to keep me from doing something potentially (probably) stupid, but I'd really like to go ahead and do it anyways. 这是一个很好的尝试,让我不要做一些潜在的(可能)愚蠢的事情,但我真的很想继续这样做。 I suspect the interpreter has some kind of lookup table its using that I can modify, but I couldn't find anything like that on Google. 我怀疑解释器有一些我可以修改的查找表,但我在Google上找不到类似的东西。 __setattr__ didn't work, either - it returned the exact same error about the attribute being read-only. __setattr__也不起作用 - 它返回了与只读属性完全相同的错误。

I'm specifically looking for a Python 2.7 solution, if that's important, although there's no reason to resist throwing in answers that work for other versions since I suspect other people in the future will look here with similar questions regarding other versions. 我特意寻找Python 2.7解决方案,如果这很重要,虽然没有理由拒绝投入适用于其他版本的答案,因为我怀疑未来的其他人会在这里看到与其他版本类似的问题。

Despite its dynamicity, Python does not allow monkey-patching built-in types such as file . 尽管它具有动态性,但它不允许使用猴子修补内置类型(如file It even prevents you to do so by modifying the __dict__ of such a type — the __dict__ property returns the dict wrapped in a read-only proxy, so both assignment to file.write and to file.__dict__['write'] fail. 它甚至阻止你通过修改这么做__dict__这种类型的-的__dict__属性返回包裹在一个只读代理的字典,这样既分配file.writefile.__dict__['write']失败。 And for at least two good reasons: 至少有两个很好的理由:

  1. the C code expects the file built-in type to correspond to the PyFile type structure, and file.write to the PyFile_Write() function used internally. C代码期望file内置类型对应于PyFile类型结构,而file.write对应于内部使用的PyFile_Write()函数。

  2. Python implements caching of attribute access on types to speed up method lookup and instance method creation. Python实现了对类型的属性访问的缓存,以加速方法查找和实例方法创建。 This cache would be broken if it were allowed to directly assign to type dicts. 如果允许直接分配类型dicts,则该缓存将被破坏。

Monkey-patching is of course allowed for classes implemented in Python which can handle dynamic modifications just fine. 当然,允许使用Python实现的类修补程序,它可以很好地处理动态修改。

However... if you really know what you are doing, you can use the low-level APIs such as ctypes to hook into the implementation and get to the type dict. 但是......如果你真的知道自己在做什么,可以使用ctypes等低级API挂钩实现并进入类型dict。 For example: 例如:

# 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')

Despite Python mostly being a dynamic language, there are native objects types like str , file (including stdout ), dict , and list that are actually implemented in low-level C and are completely static: 尽管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__'

If your object is native C code, your only hope is to use an actual regular class. 如果您的对象是本机C代码,那么您唯一的希望就是使用实际的常规类。 For your case, like already mentioned, you could do something like: 对于您的情况,如上所述,您可以执行以下操作:

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()

or, to do something similar to your original code: 或者,做一些类似于原始代码的事情:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM