0

假设我有这样的课程:

class Test(object):
    prop = property(lambda self: "property")

每当我尝试访问时,描述符优先Test().prop。所以那将返回'property'。如果我想访问对象的实例存储,我可以这样做:

x = Test()
x.__dict__["prop"] = 12
print(x.__dict__["prop"])

但是,如果我将课程更改为:

class Test(object):
    __slots__ = ("prop",)
    prop = property(lambda self: "property")

由于不再存在,我该如何做同样的事情,并访问 的内部存储x、写入12和读取它?x.__dict__

我对 Python 很陌生,但我理解 Python 的理念是提供完全控制,那么为什么实现细节阻止我这样做呢?

Python是否缺少可以从实例内部存储中读取的内置函数,例如:

instance_vars(x)["prop"] = 12
print(instance_vars(x)["prop"])

这会像vars, 除了它也适用于, 和没有?__slots__的内置类型__dict__

4

3 回答 3

3

简短的回答,你不能

问题是插槽本身是根据描述符实现的。鉴于:

class Test(object):
    __slots__ = ("prop",)

t = Test()

词组:

t.prop

大致翻译为:

Test.prop.__get__(t, Test)

whereTest.prop<type 'member_descriptor'>由运行时专门设计的,用于从实例的保留空间中加载prop值。Test

如果您在类主体定义中添加另一个描述符,它会掩盖member_descriptor可以让您访问 slotted 属性的描述符;没有办法要求它,它不再存在了。这实际上就像在说:

class Test(object):
    @property
    def prop(self):
        return self.__dict__['prop']

    @property
    def prop(self):
        return "property"

你已经定义了两次。没有办法“理解”第一个prop定义。


但:

长答案,你不能以一般的方式。你可以

您仍然可以滥用 python 类型系统来使用另一个类定义来解决它。您可以更改 python 对象的类型,只要它具有完全相同的类布局,这大致意味着它具有所有相同的插槽:

>>> class Test1(object):
...     __slots__ = ["prop"]
...     prop = property(lambda self: "property")
... 
>>> class Test2(object):
...     __slots__ = ["prop"]
... 
>>> t = Test1()
>>> t.prop
'property'
>>> t.__class__ = Test2
>>> t.prop = 5
>>> t.prop
5
>>> t.__class__ = Test1
>>> t.prop
'property'

但是没有通用的方法来内省一个实例来计算它的类布局。你只需要从上下文中知道。您可以查看它的__slots__类属性,但这不会告诉您超类中提供的插槽(如果有的话),也不会在定义类后该属性因某种原因发生更改时给您任何提示。

于 2012-05-15T13:30:45.010 回答
1

我不太明白你想做什么以及为什么要这样做,但这对你有帮助吗?

>>> class Test(object):
    __slots__ = ("prop",)
    prop = property(lambda self: "property")


>>> a = Test()
>>> b = Test()
>>> a.prop
'property'
>>> tmp = Test.prop
>>> Test.prop = 23
>>> a.prop
23
>>> Test.prop = tmp; del tmp
>>> b.prop
'property'

当然,您不能在每个实例的基础上覆盖该属性,这就是开槽描述符的全部意义所在。

请注意,具有__slots__do 的类的子类具有 a __dict__,除非您手动定义__slots__,因此您可以这样做:

>>> class Test2(Test):pass

>>> t = Test2()
>>> t.prop
'property'
>>> t.__dict__['prop'] = 5
>>> t.__dict__['prop']
5
>>> Test2.prop
<property object at 0x00000000032C4278>

但仍然:

>>> t.prop
'property'

这不是因为__slots__,而是描述符的工作方式。

__dict__在属性查找中被绕过了,你​​只是在滥用它作为恰好用于存储状态的数据结构。相当于这样做:

>>> class Test(object):
    __slots__ = ("prop", "state")
    prop = property(lambda self: "property")
    state = {"prop": prop}


>>> t.prop
'property'
>>> t.state["prop"] = 5
>>> t.state["prop"]
5
>>> t.prop
'property'
于 2012-05-15T13:00:07.627 回答
0

如果你真的想做那样的事情,并且你真的真的需要那样的事情,你总是可以覆盖__getattribute__and __setattribute__,这同样愚蠢......这只是为了向你证明:

class Test(object):
    __slots__ = ("prop",)
    prop = property(lambda self: "property")
    __internal__ = {}

    def __getattribute__(self, k):
        if k == "__dict__":
            return self.__internal__
        else:
            try:
                return object.__getattribute__(self, k)
            except AttributeError, e:
                try:
                    return self.__internal__[k]
                except KeyError:
                    raise e

    def __setattribute__(self, k, v):
        self.__internal__[k] = v
        object.__setattribute__(self, k, v)

t = Test()

print t.prop

t.__dict__["prop"] = "test"
print "from dict", t.__dict__["prop"]
print "from getattr", t.prop

import traceback
# These won't work: raise AttributeError
try:
    t.prop2 = "something"
except AttributeError:
    print "see? I told you!"
    traceback.print_exc()

try:
    print t.prop2
except AttributeError:
    print "Haha! Again!"
    traceback.print_exc()

(在 Python 2.7 上试过)我猜这正是你所期望的。不要这样做,没用的。

于 2012-05-15T13:25:30.340 回答