Final Remarks About Descriptors

Learn about the interface of descriptors, object-oriented design of descriptors, and type annotation on descriptors.

To wrap up our analysis on descriptors, we would like to share some thoughts in terms of clean code and good practices or recommendations from experience.

Descriptor interfaces

When we revisited the interface segregation principle, we said that it's good practice to keep the interfaces small, and for that reason, we might want to separate them into smaller ones.

This idea appears here once again, not in the sense of an interface as in an abstract base class, but as the interface that the descriptor itself will present.

As already mentioned, the descriptor protocol entails four methods, but partial implementation is allowed. That means we don't need to implement all of them all the time. In fact, if we only implement the minimal required methods, that would be better.

Most of the time, we'll find that we can address our requirements by only implementing the __get__ method.

Note: Don't implement more methods than are necessary. The fewer methods we can implement of the descriptor protocol, the better.

Moreover, we'll find that the __delete__ method is seldom required.

Object-oriented design of the descriptors

With this concept, we don't mean that we can improve our object-oriented design capabilities by merely using descriptors (we have already covered that). But since descriptors are just regular objects, the rules of object-oriented design apply to them as well. For example, we can have base classes of descriptors, make use of inheritance to create more specific ones, etc.

Keep in mind that all the rules and recommendations of good practices apply as well. For example, if we have a base class for a descriptor that only implements the __get__ method, then it wouldn't be a good idea to create a subclass of it that also implements the __set__ method, as it wouldn't comply with Liskov's substitution principle (because we'd have a more specific type that implements an enhanced interface that the parent doesn't provide).

Type annotations on descriptors

Applying type annotations on descriptors might be complicated most of the time.

There could be issues with circular dependencies (meaning the Python file that contains the definition for the descriptor will have to read from the file of the consumer in order to get the types, but then the client needs to read the file with the definition of the descriptor object to use it). Even if we surmount these issues with the use of strings instead of the actual types, there's another problem.

Don't implement more methods than are necessary. The fewer methods we can implement of the descriptor protocol, the better.

If we know the exact type to annotate the descriptor methods, that means the descriptor is probably only useful for one type of class. And that generally defeats the purpose of a descriptor: The recommendation of this course is to use descriptors for scenarios in which we know we can benefit from a generalization, and reuse a lot of code. If we're not reusing code, the complexity of having descriptors is not worth it.

For this reason, and even though it is generally good practice to always add annotations to our definitions, for the case of descriptors, it might be simpler just not to. Instead, think of it as a good opportunity for writing useful docstrings that accurately document the behavior of the descriptor.

Get hands-on with 1200+ tech skills courses.