5

我正在阅读来自链接的描述符如何工作的解释:http: //users.rcn.com/python/download/Descriptor.htm#properties

但是,在这里,在 classProperty__get__方法下,我对方法签名有疑问。方法签名是:

def __get__(self, obj, objtype=None):

在这里,我知道什么时候以及如何obj可以是 None 或实际对象。

但是,我无法理解:在什么情况下objtype可以None?并且,它在实际示例中如何有用。

4

5 回答 5

6

签名

def __get__(self, obj, objtype=None):

是说这objtype是一个可选参数。如果__get__仅使用一个参数调用,objtype则将设置为None.


例如,Foo 可以通过以下方式从 Bar 窃取一个方法foo.baz

class Foo(object):
    pass
class Bar(object):
    def baz(self):
        print('Hi')        

foo = Foo()
foo.baz = Bar.baz.__get__(foo)
print(foo.__dict__)
# {'baz': <bound method ?.baz of <__main__.Foo object at 0xb787006c>>}
foo.baz()
# Hi

相反,如果使用了 2 参数形式__get__

foo.baz = Bar.baz.__get__(foo, foo.__class__)

然后foo.baz未绑定的方法Bar.bazfoo.baz()引发

TypeError: unbound method baz() must be called with Bar instance as first argument (got nothing instead)

请注意,在 Python3 中, 的概念unbound method已被删除。不再检查调用 obj 的类是否是正确的类型。因此,在 Python3 中,用于定义的 1 参数和 2 参数形式都foo.baz有效。

于 2012-09-30T11:56:26.407 回答
2

也许我正在改写上述答案,但对我来说,上述想法的这种呈现似乎更容易理解。

考虑@cached_property 的这个实现

class cached_property(object):
    """Like @property, but caches the value."""

    def __init__(self, func):
        self._func = func

    def __get__(self, obj, cls):
        if obj is None:
            return self
        value = self._func(obj)
        obj.__dict__[self.__name__] = value
        return value

obj我对“为什么要检查None?”有同样的问题。和“为什么要回归自我?”

这是两种情况如何出现的示例

典型用法:

class Foo(object):
    @cached_property
    def foo(self):
        # Would usually have significant computation here
        return 9001

foo_instance = Foo()
foo_foo = foo_instance.foo # Behind the scenes invokes Foo.foo.__get__(foo_instance, Foo)

等等,是的,这就是我所期望的,那么什么时候objNone

不那么典型的用法(获取对属性的未绑定版本的访问)

(取Foo同上)

>> Foo.foo
<__main__.cached_property at 0x178bed0>

在这种情况下,调用看起来像Foo.foo.__get__(None, Foo)

于 2013-09-08T02:03:39.010 回答
1

python 文档在Implementation Descriptors下对此进行了很好的讨论。签名实际上是下面提供的。正如你提到的实例可能没有,但所有者永远不应该是无。也许您阅读的方法有误。

object.__get__(self, instance, owner)
于 2012-09-30T11:45:54.840 回答
0

objtype 表示 obj 的“所有者”类,这意味着您将实例传递给 obj 并将其基类传递给 objtype。但是,这意味着 obj 可以是 None,因为可能没有给定类的实例,但 objtype 不能,因为这会引发 AttributeError 异常。正如Marwan所说,你确定你没有把什么混在一起吗?;)

于 2012-09-30T11:48:18.067 回答
0

从https://github.com/youngsterxyf/mpdp-code/blob/master/chapter9/lazy.py重新编辑

    # lazy.py
    class LazyProperty:

        def __init__(self, method):
            self.method = method
            self.method_name = method.__name__
            #print('function overriden: {}'.format(self.method))
            #print("function's name: {}".format(self.method_name))

        def __get__(self, obj, cls):
            if not obj:
               return None
            value = self.method(obj)
            #print('value {}'.format(value))
            setattr(obj, self.method_name, value)
            return value


    class Test:

        def __init__(self):
            self.x = 'foo'
            self.y = 'bar'
            self._resource = None

        @LazyProperty
        def resource(self):
            print('initializing self._resource which is: {}'.format(self._resource))
            self._resource = tuple(range(5))
            return self._resource

    def main_Py27():
        # python 2.7.12
        t = Test()
        print(t.x)
        #>>> foo
        print(t.y)
        #>>> bar
        print(t.resource)
        #>>> <__main__.LazyProperty instance at 0x02C2E058>
        print(t.resource.__get__(t,Test))
        #>>> initializing self._resource which is: None
        #>>> (0, 1, 2, 3, 4)
        print(t.resource)
        #>>> (0, 1, 2, 3, 4)

    def main_Py3():
        # python 3.x
        t = Test()
        print(t.x)
        #>>> foo
        print(t.y)
        #>>> bar
        print(t.resource)
        #>>> initializing self._resource which is: None
        #>>> (0, 1, 2, 3, 4)
        print(t.resource)
        #>>> (0, 1, 2, 3, 4)

    def main():
        import sys
        if sys.version_info < (3,0):
            main_Py27()
        else:
            main_Py3()

    if __name__ == '__main__':
        main()
于 2016-11-10T06:00:43.763 回答