只读python属性?无法打印对象

时间:2014-01-15 20:58:01

标签: python introspection

我有cx_Oracle.Connection名为x的实例,我正在尝试print x.clientinfox.module并获取:

attribute 'module' of 'cx_Oracle.Connection' objects is not readable

(奇怪的是,我可以打印x.username

我仍然可以dir(x)取得成功,我没有时间查看cx_Oracle的源代码(很多是用C实现的),所以我想知道实现者是怎么做的能做到这一点?是滚动描述符吗?或与__getitem__相关的东西?这会是什么动机?

1 个答案:

答案 0 :(得分:2)

您可以使用自定义描述符在Python中轻松完成此操作。

查看HOWTO中的Descriptor Example。如果您只是更改__get__方法以引发AttributeError ......就是这样。我们不妨将其重命名并删除日志记录内容以使其更简单。

class WriteOnly(object):
    """A data descriptor that can't be read.
    """

    def __init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        raise AttributeError("No peeking at attribute '{}'!".format(self.name))

    def __set__(self, obj, val):
        self.val = val

class MyClass(object):
    x = WriteOnly(0, 'x')

m = MyClass()
m.x = 20 # works
print(m.x) # raises AttributeError

请注意,在2.x中,如果您忘记(object)并创建经典类,则描述符将不起作用。 (我相信描述符他们自己实际上可以是经典类......但是不要这样做。)在3.x中,没有经典类,所以这不是问题。

那么,如果值是只写的,你会怎么读它?

嗯,这个玩具示例没用。但是你可以,例如,在obj而不是你自己设置一些私有属性,此时知道数据存储位置的代码可以找到它,但偶然内省不能。


但你甚至不需要描述符。如果你想要一个只写的属性,无论你把它附加到哪个类,那都是一回事,但是如果你只是想阻止对特定类的某些成员的读访问,那么有一种更简单的方法:

class MyClass(object):
    def __getattribute__(self, name):
        if name in ('x', 'y', 'z'):
            raise AttributeError("No! Bad user! You cannot see my '{}'!".format(name))
        return super().__getattribute__(self, name)

m = MyClass()
m.x = 20
m.x # same exception

有关详细信息,请参阅文档中data model一章中的__getattr____getattribute__文档。

在2.x中,如果你离开(object)并创建一个经典类,属性查找的规则是完全不同的,并没有完全记录,你真的不想学习它们,除非你我计划在90年代花费大量时间,所以......不要这样做。此外,2.x显然需要2.x风格的显式super调用,而不是3.x风格的魔术super()


从C API方面,你有大多数相同的钩子,但它们有点不同。有关详细信息,请参阅PyTypeObject s,但基本上是:

  • tp_getset允许您使用getter和setter函数自动构建描述符,类似于@property但不相同。
  • tp_descr_gettp_descr_set分别用于构建描述符。
  • tp_getattrotp_setattro__getattr____setattr__类似,不同之处在于它们被调用的时间略有不同,您通常会调用{{1}当你知道你没有需要挂钩属性访问的基类时,而不是委托给PyObject_GenericGetAttr

仍然,为什么 你会这样做?

就个人而言,我已经完成了这样的事情,以了解有关Python数据模型和描述符的更多信息,但这并不是将其放入已发布库中的理由。

我猜测,有人这样做是因为他们试图在Python上强制错误地使用OO封装(基于传统的C ++模型) - 或者更糟糕的是,尝试构建Java风格security-by-encapsulation(如果没有安全的类加载器以及它附带的所有内容,它就无法工作)。

但是可能存在一些通用代码通过内省使用这些对象的情况,并且“欺骗”代码可能以试图欺骗人类用户的方式有用。例如,想象一个尝试pickle或JSON-ify或所有属性的序列化库。您可以轻松地将其写为忽略不可读的属性。 (当然,您可以轻松地制作它,例如,忽略以super()为前缀的属性...)

至于为什么_做到了......我从来没有看过它,所以我不知道。