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.
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).
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.
__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.
__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.
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.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With