This post originated from an RSS feed registered with Python Buzz
by Ian Bicking.
Original Post: Adaptation vs. Magic Methods
Feed Title: Ian Bicking
Feed URL: http://www.ianbicking.org/feeds/atom.xml
Feed Description: Thoughts on Python and Programming.
There are lots of magic methods in Python. Some of them are
especially magic, hooking into the underlying object model -- methods
like __getattr__, or its newer cousins like __get__
(used in descriptors),
or __getattribute__.
I'll refer to a different set of magic attributes, things like
__len__, __nonzero__, __gt__, etc. These
are generally ways for functions to allow objects to provide overrides
-- len(x) calls x.__len__()__, a > b
calls a.__gt__(b), etc. Lots of these functions also perform
some other fallbacks -- if __gt__ is not found, then
__cmp__ is tried, and finally the comparison is done based on
some obscure comparison of class names, or IDs or something. When
testing a value for truth, __nonzero__ is called, then if
that's not found __len__ (to check if it is zero length) then
if that's not found the object is considered "true".
A lot of these methods exist, both inside base Python, the standard
library, and extension modules (like ZODB). One could argue that they
are a sign of a hackish object model in Python -- Ruby is certainly an
instance of a language that does not do this sort of thing. But by
using functions (or operators) instead of directly using methods, they
do allow naive objects to be handled easily.
It occurred to me -- specifically when thinking about the __iter__
special method -- that many of these are examples of adaptation (I've been
using PyProtocols,
which is well documented and concisely distributed). In the case of
__iter__ we are adapting an object to the iterator interface.
__nonzero__ (and friends) adapts to the boolean interface,
and so on. v.__class__ could become adapt(v, IType)
(since classes are of type type).
In a strange sort of way, this could make things more
context-sensitive, like Perl. Unlike Perl the context is explicit
(using explicit interfaces, and adapting the object explicitly with
adapt()), but like Perl an object could be used and reused in
different contexts to provide different kinds of information.
Adaptation to an iterator is easy enough to consider -- how would
adapation work for __len__? Would we create a
ILength interface, to distinguish the unique semantics (but
not type!) of length? (Do we need to start annotating our numbers
with units to handle this?)
Following this further (too far?) does adaptation start to replace
traversal? Instead of out.write('text') do we say
adapt(out, IWriter)('text')? We quickly find ourselves using
a wildly different object model, and a different notion of
object-orientation.