Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python __get__ method

I am reading the explanation of how descriptors work from the link: http://users.rcn.com/python/download/Descriptor.htm#properties.

But, here, under the class Property's __get__ method, I have a doubt regarding the method signature. The method signature is:

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

Here, I know when and how the obj can be None or an actual object.

But, I could not understand: In what cases can the objtype be None? And, how it is useful in practical examples.

like image 357
GodMan Avatar asked Sep 30 '12 11:09

GodMan


People also ask

What is __ get __ in Python?

Python __get__ Magic Method. Python's __get__() magic method defines the dynamic return value when accessing a specific instance and class attribute. It is defined in the attribute's class and not in the class holding the attribute (= the owner class).

What is Python __ str __ method?

Python __str__() This method returns the string representation of the object. This method is called when print() or str() function is invoked on an object. This method must return the String object.

What does __ Add__ method do?

__add__ magic method is used to add the attributes of the class instance. For example, let's say object1 is an instance of a class A and object2 is an instance of class B and both of these classes have an attribute called 'a', that holds an integer.

What is Python __ slots __?

__slots__ is a class variable. If you have more than one instance of your class, any change made to __slots__ will show up in every instance. You cannot access the memory allocated by the __slots__ declaration by using subscription. You will get only what is currently stored in the list.


2 Answers

The signature

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

is saying that objtype is an optional argument. If __get__ is called with only one argument, then objtype will be set to None.


For example, Foo could steal a method from Bar by defining foo.baz this way:

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

If instead, the 2-argument form of __get__ had been used,

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

then foo.baz is the unbound method Bar.baz and foo.baz() raises

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

Note that in Python3 the concept of unbound method has been removed. There is no more checking to see that the calling obj's class is of the right type. So in Python3, both the 1-argument and 2-argument form for defining foo.baz works.

like image 133
unutbu Avatar answered Sep 27 '22 23:09

unutbu


Perhaps I am rewording an above answer, but this presentation of the thoughts above seemed easier to follow for me.

Consider this implementation of @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

I had the same question regarding "Why is obj checked for None?" and "Why return self?"

Here's an example of how both of the situations arise

The typical usage:

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)

Wait, yeah this is what I expect, what about the case when obj is None?

The not so typical usage (grabbing access to an unbound version of the property)

(Taking the same Foo as above)

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

In this case the call looks like Foo.foo.__get__(None, Foo)

like image 25
Anthony Sottile Avatar answered Sep 27 '22 22:09

Anthony Sottile