[Python-3000] Adaptation vs. Generic Functions

Tim Hochberg tim.hochberg at cox.net
Thu Apr 6 01:43:08 CEST 2006


Walter Dörwald wrote:

> Guido van Rossum wrote:
>
>> On 4/5/06, Walter Dörwald <walter at livinglogic.de> wrote:
>>
>>> Guido van Rossum wrote:
>>>
>>>> On 4/5/06, Tim Hochberg <tim.hochberg at cox.net> wrote:
>>>>
>>>>> I'm hoping that Walter can give some more realistic examples since I
>>>>> don't have real-world experience here. The basic idea is simply to 
>>>>> let
>>>>> an adapter give up and let the protocol try the next adapter. This 
>>>>> could
>>>>> happen in a generic function, for instance, if you wanted to try some
>>>>> fast algorithm for some specific subtypes, but the algorithm might be
>>>>> inappropriate depending on the values of the subtypes. You don't find
>>>>> that out till your in the adapter itself.
>>>>
>>>> Hm... The alternative, of course, instead of some magic feature, is to
>>>> explicitly call the fallback algorithm. That seems totally reasonable
>>>> for this example, and is similar to Python's single-inheritance-style
>>>> explicit super-call e.g. return BaseClass.methodName(self,
>>>> arguments...).
>>>
>>> For methods you know what your bases classes are,
>>
>>
>> Not necessarily in the case of multiple inheritance; that's why Python
>> 2.2 added super().
>
>
> True.
>
>>>  but for adapters
>>> that's not necessarily so. Somewhat could have registered another
>>> adapter for the base class.
>>
>>
>>>> Does anybody know if Phillip Eby's generic functions support this?
>>>
>>> If I understood Ian's posting
>>> (http://mail.python.org/pipermail/python-3000/2006-April/000481.html)
>>> correctly, they do.
>>>
>>>> I
>>>> would think that the definition of "next candidate" can be problematic
>>>> there, in case you've broken a tie between two ambiguous possibilities
>>>> by registering an explicit tie-breaker. Remove the tie-breaker and
>>>> neither alternative dominates the other, so the next candidate is
>>>> undefined. This is different from Python's MRO calculations, which
>>>> always create a perfect linearization of the base classes.
>>>
>>> BTW, another useful feature might be the ability to copy the protocol.
>>> Then I could register a few other adapters for the copy and use the
>>> modified copy for my own purpose.
>>
>>
>> Let's not go wild with features;
>
>
> OK! (Adding a copy method is trivial anyway).
>
>> let's first collect use cases.
>
>
> For a potential use case see http://bugs.python.org/1351692
>
> Someone wanted to add hex output for ints and long to pprint.pprint(). 
> If pprint used a protocol, he could have copied the pprint protocol 
> object, replaced the adapter for int and long and used that.
>
> But maybe a better option would be to "subclass" it, so that if the 
> adapter isn't found in the subclassed protocol it is searched in the 
> base protocol. This has the advantage that the copied protocol picks 
> up changes in the base protocol.

It seems like you can chain protocols like that simply by setting up an 
appropriate default_adapter method. For example, if we already had a 
pprinter protocol, we could define one that subclassed it and customized it:

class CustomPPrintProtocol(Protocol):
     def default_adapter(self, *args):
         return pprinter(*args)
hex_pprinter = CustomPPrintProtocol("hex_pprinter")

@hex_pprinter.register_for(int, long):
def hex_pprint(obj):
    return hex(obj)


While on the subject of pretty print, I just hacked pretty print to 
allow site wide customizations using Protocols. In addition to adding 
the protocol class wholesale (it's not on my python path right now), it 
took only three lines worth of changes. I added:

    simple_formatter = Protocol('simple_formatter')
    simple_formatter.register(repr, object)


Near the top and changed _safe_repr to use simple_formatter:

    def _safe_repr(object, context, maxlevels, level):
        # ....
        rep = simple_formatter(object) # was repr(object)
        return rep, (rep and not rep.startswith('<')), False


With that in place, I can easily do sitewise changes to stuff like 
integer display:

    import pprint
    data = ( # From fbots librarybook
        "this is a string", [1, 2, 3, 4], ("more tuples",
        1.0, 2.3, 4.5), "this is yet another string"
        )
          
    pprint.pprint(data)
    print
    pprint.simple_formatter.register(hex, int, long)
    pprint.pprint(data)

==>

    ('this is a string',
     [1, 2, 3, 4],
     ('more tuples', 1.0, 2.2999999999999998, 4.5),
     'this is yet another string')

    ('this is a string',
     [0x1, 0x2, 0x3, 0x4],
     ('more tuples', 1.0, 2.2999999999999998, 4.5),
     'this is yet another string')

Nice!

I expect that with another dozen lines of code I could make custom 
pretty printers possible, although the way the customization happens 
might be a little kludgy. I'll look into it.

-tim





More information about the Python-3000 mailing list