1.1.3.2 Replacing Introspection with Adaptation

To summarize: don't type check.

-- Alex Martelli, on comp.lang.python

Component adaptation is intended to completely replace all non-cooperative introspection techniques, such as type(), isinstance(), hasattr(), and even interface checks. Such introspection tends to limit framework flexibility by unnecessarily closing policies to extension by framework users. It often makes code maintenance more difficult as well, since such checks are often performed in more than one place, and must be kept in sync whenever a new interface or type must be checked.

Some common use cases for such introspection are:

Obviously, the first case is handled quite well by adapt(), at least in an environment where it's easy to declare adapters between types and protocols. The second and third cases may at first seem to demand an ability to introspect what interfaces are supported by a component. But they are almost always better served by defining new protocols that supply the required behavior or metadata, and then requesting implementations of those protocols.

In all three use cases, replacing introspection with adaptation opens the framework to third party extensions, without further modifications being required - and without the need to do extensive design or documentation of a new hook or extension point to be added to the framework. Indeed, the availability of a standard mechanism for adaptation means that the extension mechanism need only be documented once: right here in this document.

In section 1.1.11, we will present techniques for refactoring all three kinds of introspection code to purely adaptation-driven code, showing how the flexibility and readability of the code improves in the process. But first, we will need to cover how protocols and interfaces can be defined, declared, and adapted, using the API provided by the protocols package.

See Also:

isinstance() Considered Harmful
A brief critique of common justifications for using introspection