On 01Jan2021 16:34, Steven D'Aprano <st...@pearwood.info> wrote:
>This isn't so much an idea for Python, as a request for ideas to solve a
>problem in Python.
>
>Back in the early days of Python, printing recursive lists could crash
>the interpreter:
>
>    a = [1, 2, 3]
>    a.append(a)
>    print(a)
>
>If you try that today, you get a nice display:
>
>    [1, 2, 3, [...]]
[...]
>Is there something we can do to make it easier for people to deal with
>recursive data structures, like the list repr and deepcopy code does? Is
>there a pattern we can abstract out and provide as a tool or recipe for
>people to use in their own code?

I don't know about a tool - that might be tricky to generalise - but my 
standard pattern for this is:

    def recurse(o, seen=None):
        if seen is None:
            seen = set()
        if id(o) in seen:
            return
        seen.add(id(o))
        walk o and call recurse(sub_object, seen) ...

The key to store in seen isn't always id(o), and for some tasks I 
discard the key on my way back out of the recursion.

I can imagine writing a decorator for this. I'd probably like it to look 
like this:

    @no_recurse(keyfunc=None,popkey=False):
    def recurse(o, *, seen):
        ...
        recurse(sub_object, seen)

maybe (untested):

    @decorator
    def no_recurse(func, keyfunc=None, popkey=False):
        if keyfunc is None:
            keyfunc = id
        def no_recurse_wrapper(o, *a, seen=None, **kw):
            if seen is None:
                seen = set()
            k = keyfunc(o)
            if k in seen:
                return None
            seen.add(k)
            try:
                return func(o, *a, seen=seen, **kw)
            finally:
                if popkey:
                    seen.discard(k)

The "@decorator" decorator is one of my own which decorates a decorator 
which may accept parameter of not, allowing both:

    @no_recurse
    def recurse(o, blah..., *, seen, other_kwargs...):

and also:

    @no_recurse(keyfunc=lambda o: o.idkey())
    def recurse(o, blah..., *, seen, other_kwargs...):

for some nondefault decoration.

There are some missing features there: what to return when recusion is 
encoutered (rather than None above), some mode to make the occurrence of 
recursion trivially noticeable by the primary function to do something 
better than "return None", etc etc.

Cheers,
Cameron Simpson <c...@cskk.id.au>
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/CFZNLRQJENSDQLJ5F456F7G6ZHINSGMV/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to