首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >读取__slots__的内置函数

读取__slots__的内置函数
EN

Stack Overflow用户
提问于 2012-05-15 20:37:19
回答 3查看 1K关注 0票数 0

假设我有一个这样的类:

代码语言:javascript
复制
class Test(object):
    prop = property(lambda self: "property")

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

代码语言:javascript
复制
x = Test()
x.__dict__["prop"] = 12
print(x.__dict__["prop"])

但是,如果我将类更改为:

代码语言:javascript
复制
class Test(object):
    __slots__ = ("prop",)
    prop = property(lambda self: "property")

既然12已不复存在,我该如何做同样的事情,并访问x.__dict__的内部存储,以写入并读回它?

我是Python的新手,但我理解Python的理念是完全控制,那么为什么实现细节会阻止我这样做呢?

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

代码语言:javascript
复制
instance_vars(x)["prop"] = 12
print(instance_vars(x)["prop"])

它的工作方式与vars类似,不同之处在于它还适用于__slots__以及没有__dict__的内置类型

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-05-15 21:30:45

简短的回答:You‘t

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

代码语言:javascript
复制
class Test(object):
    __slots__ = ("prop",)

t = Test()

这句话:

代码语言:javascript
复制
t.prop

翻译过来,大约是:

代码语言:javascript
复制
Test.prop.__get__(t, Test)

其中,Test.prop是一个由运行时定制的<type 'member_descriptor'>,专门用来从Test实例的保留空间中加载prop值。

如果在类体定义中添加另一个描述符,它将屏蔽允许您访问时隙属性的member_descriptor;没有办法请求它,它已经不在那里了。这实际上就像是在说:

代码语言:javascript
复制
class Test(object):
    @property
    def prop(self):
        return self.__dict__['prop']

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

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

但是:

很长的答案,你不能在一般的方式中你可以

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

代码语言:javascript
复制
>>> 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__类属性,但这不会告诉您超类中提供的插槽(如果有的话),也不会给您任何提示,如果属性在类定义后由于某种原因发生了更改。

票数 3
EN

Stack Overflow用户

发布于 2012-05-15 21:00:08

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

代码语言:javascript
复制
>>> 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__,因此您可以这样做:

代码语言:javascript
复制
>>> class Test2(Test):pass

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

但仍然是这样:

代码语言:javascript
复制
>>> t.prop
'property'

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

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

代码语言:javascript
复制
>>> 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'
票数 1
EN

Stack Overflow用户

发布于 2012-05-15 21:25:30

如果你真的想做这样的事情,你真的需要这样的东西,你总是可以覆盖__getattribute____setattribute__,它就是一样愚蠢……这只是为了证明给你看:

代码语言:javascript
复制
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()

(在Python2.7上试用)我想这正是您所期望的。别这么做,没用的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10600813

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档