Python __get__ method

I am reading an explanation of how descriptors work by reference: http://users.rcn.com/python/download/Descriptor.htm#properties .

But, here in the class Property __get__, I have doubts about the signature of the method. Method Signature:

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

Here I know when and how objNone or the actual object can be.

But I could not understand: in what cases can <<24> be None? And, as it is useful in practical examples.

+5
source share
5 answers

Signature

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

says it objtypeis an optional argument. If __get__called with only one argument, it objtypewill be set to 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

__get__,

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

foo.baz - unbound Bar.baz foo.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 .

+6

python . , . , , None, . , , .

object.__get__(self, instance, owner)
+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)

+1

objtype represents the owner class of obj, that is, you pass an instance of obj and its base class to objtype. However, this means that obj can be None, since there cannot be an instance of this class, but objtype cannot, because this will throw an AttributeError exception. As Marwan said, are you sure you didn’t mix anything there ?;)

0
source

edited from 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 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()
0
source

All Articles