Reputation: 153311
I get the following example from Effective Python item 31:
from weakref import WeakKeyDictionary
class Grade(object):
def __init__(self):
self._values = WeakKeyDictionary()
def __get__(self, instance, instance_type):
if instance is None: return self
return self._values.get(instance, 0)
def __set__(self, instance, value):
if not (0 <= value <= 100):
raise ValueError('Grade must be between 0 and 100')
self._values[instance] = value
# Example 16
class Exam(object):
math_grade = Grade()
writing_grade = Grade()
science_grade = Grade()
first_exam = Exam()
first_exam.writing_grade = 82
second_exam = Exam()
second_exam.writing_grade = 75
print('First ', first_exam.writing_grade, 'is right')
print('Second', second_exam.writing_grade, 'is right')
I can't think of any reason to have if instance is None: return self
in __get__
. How can an Exam
(or other potential classes using Grade
) instance be None
?
Upvotes: 6
Views: 2226
Reputation: 1121744
Python will pass in None
for the instance when accessing the descriptor on the class.
By returning self
in that case you can access the descriptor object on the class without having to bypass the protocol (by accessing ClassObj.__dict__['name_of_descriptor']
).
>>> class DemoDescriptor:
... def __get__(self, instance, type_):
... if instance is None:
... print('Accessing descriptor on the class')
... return self
... print('Accessing descriptor on the instance')
... return 'Descriptor value for instance {!r}'.format(instance)
...
>>> class DemoClass(object):
... foo = DemoDescriptor()
...
>>> DemoClass.foo # on the class
Accessing descriptor on the class
<__main__.DemoDescriptor object at 0x1041d3c50>
>>> DemoClass.__dict__['foo'] # bypassing the descriptor protocol
<__main__.DemoDescriptor object at 0x1041d3c50>
>>> DemoClass().foo # on the instance
Accessing descriptor on the instance
'Descriptor value for instance <__main__.DemoClass object at 0x1041d3438>'
This is how the __get__
method implementations for function
and property
objects work too.
For your specific case, each of Exam.math_grade
, Exam.writing_grade
or Exam.science_grade
will call Grade.__get__
, passing in None
for the instance, and Exam
for the instance_type
.
Upvotes: 7