python __get__方法

时间:2012-09-30 11:25:03

标签: python descriptor getattribute

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

但是,在这里,在类Property的{​​{1}}方法下,我对方法签名有疑问。方法签名是:

__get__

在这里,我知道def __get__(self, obj, objtype=None):何时以及如何可以是无或实际对象。

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

5 个答案:

答案 0 :(得分:6)

签名

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

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


例如,Foo可以通过这种方式定义foo.baz来从Bar中窃取方法:

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

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

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中, 用于定义foo.baz的1参数和2参数形式都可以。

答案 1 :(得分:1)

python docs在Implementing Descriptors下讨论了这个问题。签名实际上是下面提供的。如你所提到的那样,实例可能是None,但所有者永远不应该是None也许在你如何读书时有一个错误。

object.__get__(self, instance, owner)

答案 2 :(得分:1)

也许我正在重写上面的答案,但上面提到的这个想法对我来说似乎更容易理解。

考虑@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)

等等,是的,这就是我的期望,obj None时的情况怎么样?

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

(采取与上述相同的Foo

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

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

答案 3 :(得分:0)

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

答案 4 :(得分: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()