内置函数读取__slots__

时间:2012-05-15 12:37:19

标签: python descriptor

假设我有一个这样的课程:

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__

3 个答案:

答案 0 :(得分:3)

简短回答,你不能

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

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

t = Test()

短语:

t.prop

翻译成大约:

Test.prop.__get__(t, Test)

其中Test.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__类属性,但这不会告诉您超类中提供的插槽(如果有的话),如果该属性已更改,它也不会给您任何提示由于某种原因,在课程定义后。

答案 1 :(得分: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__的类的子类确实有__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'

答案 2 :(得分:0)

如果你真的想做类似的事情,而你真的需要这样的东西,你总是可以覆盖__getattribute____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上尝试过) 这正是你所期待的。不要这样做,这没用。