Download - Descriptors In Python
amitu.com
Descriptors In Python
Excerpt from Python For Programmers available on amitu.com/python/
amitu.com Descriptors In Python
This is an excerpt of my upcoming python book
amitu.com/python/
amitu.com Descriptors In Python
Lets take a look at property in Python
amitu.com Descriptors In Python
Or with a @property syntax sugar
amitu.com Descriptors In Python
So what exactly is the property() returning?
amitu.com Descriptors In Python
And why don't we get what property() returns when we access .first_name?
student.first_name appears to be simple string object!
amitu.com Descriptors In Python
And assigning to a property is even weirder.
It doesn't seem to be overwriting anything, as normally assignment does.
It calls a function! The setter.
What were you smoking Guido?!?
amitu.com Descriptors In Python
We know that properties are useful, no complaints here.
The real question is…
… is this some compiler/interpreter magic?
or
is it application of some simple feature available to us?
amitu.com Descriptors In Python
We know the @ syntax is just a syntax sugar, we love it, we create decorators
using them.
Out of @property()…
Which leaves us with property() function or decorator.
Is property() special, or can we write it ourselves?
amitu.com Descriptors In Python
… and the answer is YES!
The name of the feature is descriptor.
Django’s ORM uses it extensively for example…
amitu.com Descriptors In Python
Lets recap what happens when Python sees attribute access:
[of course we are simplifying thins, no __mro__, no __call__ etc etc]
amitu.com Descriptors In Python
… but the picture is closer to:
(we are reusing the function defined in previous slide)
amitu.com Descriptors In Python
… or in English:
If on an object, say o, when you do an attribute lookup, say o.x, if Python finds an object at o.x that has o.x.__get__ attribute, then instead of
returning o.x, it returns whatever o.x.__get__(o, o.__class__) returns.
amitu.com Descriptors In Python
… when doing setting an attribute, (o.x = 10), o.x__set__(o, 10) is looked up and called …
And this is not only for reading an attribute…
… and when you call del o.x, o.x.__delete__(obj, o.__class__) is called if
present.
amitu.com Descriptors In Python
With that in mind, lets implement @property:
Doesn’t look that hard, does it?
amitu.com Descriptors In Python
Lets use our MyProperty:
Works like a charm!
amitu.com Descriptors In Python
When better_lookup() sees .first_name, it finds instance of our class, and our class
instances do have .__get__(), so Python calls it, which calls ._getter(), that was passed to
MyProperty() constructor (.__init__()) because we used it as a decorator!
amitu.com Descriptors In Python
Please note:
MyProperty class instance is created when Student class is being constructed (as against
when student instance is being initialised).
amitu.com Descriptors In Python
Lets try setter:
amitu.com Descriptors In Python
Using the setter:
amitu.com Descriptors In Python
How we implemented @name.setter is by realising that name now points to an
instance of our MyProperty class, and that instance happened to have .setter()
method, which happens to take a function as argument, so name.setter can be used
as a decorator too.
amitu.com Descriptors In Python
Cool, so we can implement @property using descriptors.
Next question is:When to use descriptor over property?
amitu.com Descriptors In Python
The deciding factor:
The implementation of the property lives inside the class that is using the property.
The descriptor on the other hand is a separate class altogether.
Let me clarify…
amitu.com Descriptors In Python
Say we want to implement .name on a bunch of classes.
1. It must convert assigned names to UPPER case.
2. It must validate that name is composed of two words.
3. Name must be maximum of 100 chars long.
amitu.com Descriptors In Python
Say we want .name on Student, Parent and Teacher classes, and they have nothing else
in common.
amitu.com Descriptors In Python
One solution is we implement a NamedObject, and subclass each of Student, Parent and
Teacher from NamedObject:
and so on for Parent and Teacher
amitu.com Descriptors In Python
The property based solution works, but it does not “scale”.
What if we don't just have .name, but also .spouse_name on Student?
What if along with .name and .spouse_name, we also want different
length validations on each name, .name must be 100 chars, but .spouse_name upto
80 chars?
amitu.com Descriptors In Python
A property based solution would require us to keep track
of ._name, ._spouse_name, ._name_length and ._spouse_length on
Student object.
amitu.com Descriptors In Python
Lets see how to implemented this using descriptors?
amitu.com Descriptors In Python
As you can see descriptor scales pretty well, encapsulates better.
We can even subclass Name descriptor to add more features to it, and we wont have to touch
the Student class etc.
amitu.com Descriptors In Python
A note on descriptors as class attributes:
If we try to access a descriptor using the class instead of instance, e.g. Student.name instead of
student.name (where student = Student()), .__get__() is still called with obj set to None.
amitu.com Descriptors In Python
… and at class level .__set__() and .__delete__() calls are not allowed.
amitu.com Descriptors In Python
Thats it for now!
You have seen a section in my Python for Developers book. This covers python
material for people who're already developers, even python developers.
Check it out: amitu.com/python/