In this chapter, we saw several other ways of altering the meaning and execution of Python code, allowing us to conform the language to our specialized needs.
We saw how function decorators use functions as input data for manipulation. We took a look at function annotations and particularly how they interact with function decorators. We saw how class decorators work exactly as function decorators do, but because they operate on classes, the possibilities are very different. We saw how to modify classes, wrap them, or even replace them using decorators. We discussed how to use a metaclass to affect the construction of a class object and how to make unusual behavior inheritable by making it part of a class's metaclass. We looked at context manager, both synchronous and asynchronous. We saw how context managers work and learned how to make our own for use in either synchronous...