python中描述符概念的行为(令人困惑)


问题内容

我了解python描述符,但对此有些困惑。

如果您具有以下类描述符

class Descriptor(object):
    def __get__(self, instance, owner):
        print 'getting'
        return self.value
    def __set__(self, instance, value):
        print 'setting'
        self.value = value
    def __delete__(self, instance):
        print 'deleting'
        del self.value

我们要管理其属性的类是这样的。

class Test(object):
    name = Descriptor()
    def __init__(self, name):
        print 'init test'
        self.name = name

当我创建Test类的对象并执行某些操作时,它会给我这样的答案…

t = Test('abc')
init test
setting
>>> t.name
getting 
'abc'
>>> del t.name
deleting
>>> t
<__main__.Test object at 0x013FCCD0>
>>> t.name
getting

现在我想要一个类Test1像这样。

class Test1(object):
    def __init__(self, value):
        print 'init test1'
        self.name = Descriptor()
        self. value = value

如果我创建Test1的对象并尝试访问Test1实例的属性,则会得到类似以下的输出。

t1 = Test1(12)
t1.name
>>> getting
>>> 12
>>> t1.name = 30
>>> setting

Q 1)我的问题是这个名称属性是否在Test1的init中声明,是否绑定到Test1的实例…因为当我尝试获取t1的属性字典时,它返回空dict …

t1.__dict__
>>> {}

与类Test的实例t相同

 t.__dict__
 >>> {}

当我向这些实例中的任何一个实例添加新属性时,就像这样…

 t.some = 'some'
 >>> t1.some = 'some'

再一次,如果我尝试访问属性字典,它只会给我刚才添加的属性..现在所有实例属性

t.__dict__
>>> {'some': 'some'}
>>> t1.__dict__
>>> {'some': 'some'}

问题2)那么init中定义的实例属性(如类Descriptor和Test中的变量名称和值)与实例创建后定义的属性(如变量t.some)之间有什么区别?

问3)类Test与类Test1有何不同。


问题答案:

Test1Descriptor未真正用作描述符的情况下,它只是一个称为的普通属性name,碰巧有一些特殊方法。但这还不是真正的描述符。

如果您阅读有关如何调用描述符的文档,则会看到用于调用描述符方法的机制。在您的情况下,这将意味着t.name大致相当于:

type(t).__dict__['name'].__get__(t, type(t))

t1.name

type(t1).__dict__['name'].__get__(t1, type(t1))

name__dict__在类的而不是实例中查找的,因此区别在于Test1.__dict__没有称为的描述符name

>>> Test.__dict__['name']
<__main__.Descriptor object at 0x7f637a57bc90>
>>> Test1.__dict__['name']
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
KeyError: 'name'

您还应该考虑的是,描述符value本身设置了属性,这意味着的所有实例Test将共享相同的值:

>>> t1 = Test(1)
init test
setting
>>> t2 = Test(2)
init test
setting
>>> t1.name
getting
2
>>> t2.name
getting
2
>>> t1.name = 0
setting
>>> t2.name
getting
0

我认为您迫切希望做的是设置valueinstance而不是那样self,这样可以使您获得预期的行为Test