[Python-3000] Generic functions

Walter Dörwald walter at livinglogic.de
Tue Apr 4 20:11:04 CEST 2006


Ian Bicking wrote:

> Walter Dörwald wrote:
>> What happens, if I do the following
>>
>> @PrettyPrinter.pformat.when(object=list)
>> def foo(...):
>>    ...
>>
>> @PrettyPrinter.pformat.when(object=object)
>> def foo(...):
>>    ...
>>
>> How does it know which isinstance() check to do first?
> 
> It can see that isinstance(list, object) is true, and orders the checks
> accordingly.  If the condition is entirely ambiguous it raises an error.
>  At least, this is how RuleDispatch works.  It has some knowledge of
> operations like isinstance so that it can tell that the set of objects
> that are instances of list is a strict subset of the objects that are
> instances of object.  Anyway, for the specific case of type-based
> dispatch it's fairly straight forward to figure out what condition is
> most specific.  Though, say, if there's a specialized method for (A,
> object) and a specialized method for (object, B) and you call with (A,
> B) that's just ambiguous, and without an explicit resolution it should
> be an error.

RuleDispatch seems to contain some fairly magical code.

But for dispatching on the type of one object, traversing the mro and
looking up each type in the registry is sufficient and probably fast
enough too.

> I guess another take on this, that precedes RuleDispatch, is multiple
> dispatch, like in
> http://www-128.ibm.com/developerworks/linux/library/l-pydisp.html -- the
> article also precedes the decorator syntax I guess (or just doesn't use
> it); I think the decorator syntax makes this look much nicer.  Though
> single-argument type-based dispatch is probably the most common case.
> 
>> And what happens with performance if I have registered many handler
>> functions?
> 
> Depends on the implementation; I believe RuleDispatch builds a decision
> tree.
> 
>>> [...]
>>> The implementation of my simplistic form of generic function isn't
>>> too hard.  Ignoring keyword arguments, it might work like:
>>>
>>> class generic(object):
>>>      def __init__(self, func):
>>>          self.func = func
>>>          self.registry = {}
>>>      def __call__(self, *args):
>>>          for pattern, implementation in self.registry.items():
>>>              for passed, expected in zip(args, pattern):
>>>                  # None is a wildcard here:
>>>                  if (expected is not None and
>>>                      not isinstance(passed, expected)):
>>>                      break
>>>              else:
>>>                  return implementation(*args)
>>>          return self.func(*args)
>>>      def when(self, *args):
>>>          def decorator(func):
>>>              self.registry[args] = func
>>>              return func
>>>          return decorator
>>>      def __get__(self, obj, type=None):
>>>          if obj is None:
>>>              return self
>>>          return types.MethodType(self, obj, type)
>>>
>>> There's lots of details, and handling keyword arguments, dealing
>>> intelligently with subclasses, and other things I probably haven't
>>> thought of.  But anyway, this allows:
>>>
>>> class PrettyPrinter:
>>>      def pformat(self, object): ...
>>>
>>> # Without keyword arguments I have to give a wildcard for the self
>>> # argument...
>>> @PrettyPrinter.pformat(None, list)
>>> def pformat_list(self, object):
>>>      ...
>>
>>
>> I don't understand! There's no generic in sight here!
> 
> Maybe I should have called this more modest implementation simplistic
> multiple dispatch. In this case it is dispatching on the second
> argument (the first is "self" and not as interesting).  pformat_list is
> an implementation for objects of type list, PrettyPrinter.pformat is a
> fallback implementation.

The way the code is written PrettyPrinter.pformat is a simple unbound
method. Was

class PrettyPrinter:
    def pformat(self, object): ...

supposed to be

class PrettyPrinter:
    @generic
    def pformat(self, object): ...

or something similar?

Bye,
   Walter Dörwald

> 



More information about the Python-3000 mailing list