Descriptors In Python

Post on 12-Apr-2017

86 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

Transcript

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/

top related