1.1.3 adapt() and the Adaptation Protocol
Description: adapt() and the Adaptation Protocol
Keywords: ref

##
1.1.3 `adapt()` and the Adaptation Protocol

Component adaptation is the central focus of the `protocols` package. All of the package's protocol declaration API depends on component adaptation in order to function, and the rest of the package is just there to make it easier for developers to use component adaptation in their frameworks and programs.

Component adaptation is performed by calling the `adapt()` function, whose design is based largely on the specification presented in PEP 246:

`adapt`( | component, protocol, [, default])

  Return an implementation of protocol (a protocol object) for component (any object). The implementation returned may be component, or a wrapper that implements the protocol on its behalf. If no implementation is available, return default. If no default is provided, raise `protocols.AdaptationFailure`.

The component adaptation process performed by `adapt()` proceeds in four steps:

1. If protocol is a class or type, and component is an instance of that class or type, the component is returned unchanged. (This quickly disposes of the most trivial cases).

2. If component has a `__conform__` method, it is called, passing in the protocol. If the method returns a value other than `None`, it is returned as the result of `adapt()`.

3. If protocol has an `__adapt__` method, it is called, passing in component. If the method returns a value other than `None`, it is returned as the result of `adapt()`.

4. Perform default processing as described above, returning default or raising `protocols.AdaptationFailure` as appropriate.

This four-step process is called the adaptation protocol. Note that it can be useful even in the case where neither the component nor the protocol object are aware that the adaptation protocol exists, and it gracefully degrades to a kind of `isinstance()` check in that case. However, if either the component or the protocol object has been constructed (or altered) so that it has the appropriate `__conform__` or `__adapt__` method, then much more meaningful results can be achieved.

Throughout the rest of this document, we will say that a component supports a protocol, if calling `adapt(component,protocol)` does not raise an error. That is, a component supports a protocol if its `__conform__` method or the protocol's `__adapt__` method return a non-`None` value.

This is different from saying that an object provides a protocol. An object provides a protocol if `adapt(ob,protocol) is ob`. Thus, if an object provides a protocol, it supports the protocol, but an object can also support a protocol by having an adapter that provides the protocol on its behalf.

Now that you know how `adapt()` works, you can actually make use of it without any of the other tools in the `protocols` package. Just define your own `__conform__` and `__adapt__` methods, and off you go!

In practice, however, this is like creating a new kind of Python ``number'' type. That is, it's certainly possible, but can be rather tedious and is perhaps best left to a specialist. For that reason, the `protocols` package supplies some useful basic protocol types, and a ``declaration API'' that lets you declare how protocols, types, and objects should be adapted to one another. The rest of this document deals with how to use those types and APIs.

You don't need to know about those types and APIs to create your own kinds of protocols or components, just as you don't need to have studied Python's numeric types or math libraries to create a numeric type of your own. But, if you'd like your new types to interoperate well with existing types, and conform to users' expectations of how such a type behaves, it would be a good idea to be familiar with existing implementations, such as the ones described here.