Python __get__静态实例和成员之间的差异

时间:2016-07-17 22:07:08

标签: python

我无法理解在静态对象和成员对象(在构造函数中创建的对象)中发生了什么变化。

以下内容将运行被覆盖的获取():

class A(object):
    class B(object):
        def __init__(self, initval=None, name='var'):
            self.val = initval
            self.name = name

        def __get__(self, obj, objtype):
            print('B Retrieving', self.name)
            return self.val

    b = B(10, 'var "b"')

但是,如果我将b拉入构造函数,则不会:

class A(object):
    class B(object):
        def __init__(self, initval=None, name='var'):
            self.val = initval
            self.name = name

        def __get__(self, obj, objtype):
            print('B Retrieving', self.name)
            return self.val

    def __init__(self)):
        self.b = A.B(10, 'var "b"')

我真的想在后者中完成这项工作,也许这不是正确的方法。

有人可以通过致电print(a.b) a = A()来解释这里发生的事情吗?

另外,有没有办法让print(a.b)从b调用成员函数?

2 个答案:

答案 0 :(得分:2)

通过实施__get__,您将班级B变为descriptor class。描述符是通过在实例上执行自定义逻辑来处理属性访问的对象。

为了使描述符起作用,您需要将它们声明为该类型的成员。只有这样,Python才能正确调用对象的__get____set__方法。

执行self.b = SomeDescriptor()不起作用的原因是因为通过向self.b分配内容,您直接更改了对象的基础__dict__

>>> class A(object): pass
>>> x = A()
>>> x.b = B(10, '')
>>> x.__dict__
{'b': <__main__.B object at 0x000000437141F208>}

正如您所看到的,x.b具有该B对象的值。那是描述符对象。当您尝试访问x.b时,您只需返回该描述符对象。永远不会调用__get__

然而,当您在类型上设置描述符时,对象的b中不存在成员__dict__,因此Python将在继承链中进一步查找并找到{的描述符对象{1}}此时它还将执行描述符:

b

答案 1 :(得分:0)

您的b对象是描述符。这意味着当Python被绑定为类变量时,Python对待它的方式与大多数其他对象不同。具体来说,在您的类变量代码中,A().b变为函数调用A.__class__.__dict__["b"].__get__(A(), A)。 Python在内部使用描述符。它是方法绑定和大多数__magic__方法如何工作的一部分。

当描述符存储为实例变量时,不给出特殊处理。查找一个实例变量,该描述符只是获取描述符对象,而不是调用其__get__方法的结果。

如果您需要能够在每个实例的基础上设置描述符返回的值,您可能希望让描述符检查它被调用的实例上的属性,而不是属性本身:

class B(object):
    def __init__(self, name='var'):
        self.name = name

    def __get__(self, obj, objtype):
        print('B Retrieving', self.name)
        return obj._bval      # lookup the value on the object, not on the descriptor

class A(object):
    b = B()                   # assign the descriptor as an class variable
    def __init__(self):
        self._bval = 10       # initialize the value

请注意,我已经取消了您的课程,主要是为了清晰起见。在B中定义A类并不严格是错误的,但它令人困惑,并没有提供使B成为顶级类的真正好处。