[Python-3000] Adaptation vs. Generic Functions

Tim Hochberg tim.hochberg at ieee.org
Wed Apr 5 19:20:30 CEST 2006


Nick Coghlan wrote:
> Tim Hochberg wrote:
> 
>>I like this version. The naming seems an improvement and the 
>>default_adapter seems like a worthy addition. I do keep wondering if 
>>there's some reason for a user of Protocol to call candidate_keys 
>>directly or it's only an implementation detail. If the there is such a 
>>reason, and we could figure it out, it would probably immediately 
>>obvious what to call it. If there isn't, perhaps it should be prefixed 
>>with '_' to indicate that it's not part of the public interface.
> 
> 
> It was a toss-up, but even if it was private, it would still need to be 
> documented so subclasses knew how to override it. And if it's public, then it 
> might be a useful introspection tool on protocols. For example:
> 
> def can_adapt(protocol, *args):
>      for key in protocol.candidate_keys(args):
>          if key in protocol.registry:
>              return True
>      return False
> 
> Although, now that I think about it, mere presence in the registry doesn't 
> necessarily signify much - it's trivial to register an "anti-adapter" that 
> raises TypeError. So having this method be public may make it an attractive 
> nuisance, rather than something useful. . .

Perhaps one could avoid this by providing a predefined AntiAdapter class 
and recomending that instances of that class (and possibly its 
sublasses) be used to disable adaption. Then something like can_adapt 
could return False rather than True if the first adapter it came across 
is an instance of AntiAdapter. (I think a better name than AntiAdapter 
might be in order though).

Let me float another name for candidate_keys: 'signatures'. Let me try 
to explain protocols in terms of signatures and see how it goes.

A protocol adapts an object by examining each of the object's signatures 
in turn and checking whether an adapter has been registered from that 
signature. If so, the result of found_adapter(the_object) is returned. 
If no adapters are found for any of the signatures, the protocol returns 
self.default_adapter(the_object). An object's signatures can vary by 
protocol, but are type(obj).__mro__ in the default case.

[I've neglected the multiargument case for now to avoid twisting myself 
into knots writing "object or objects" all over the place]

That doesn't seem too bad. Certainly better than talking about an 
object's keys, which really doesn't make any sense. However, it's 
possible that this use of signature will be confusing because it 
conflicts with the terminology "function signature".

The resulting change in __call__ results in a slightly clearer code IMO:

      def __call__(self, *args):
          """Adapt supplied arguments to this protocol"""
          for key in self.signatures(args):
              try:
                  adapter = self.registry[key]
              except KeyError:
                  pass
              else:
                  return adapter(*args)
          return self.default_adapter(*args)


Regards,

-tim






More information about the Python-3000 mailing list