I may have misunderstood something.
The original post in this subject sounded to ME likethey had nested 
dictionaries and wanted to be ableto ask a method in the first dictionary 
totake an unspecified number of arguments thatwould be successive keys and 
return the results.
I mean if A was a dictionary containing saycities and it had an alphabetical 
index of lettersA to Z and those contained dictionaries ofsay last names as 
additional dictionaries andso on, then they wanted to perhaps say;
A.getdeep("Boston", "S", "Smith", "Address", default="None")
But the replies I am seeing look so different that I mayhave missed something 
as it seems more about usingpattern matching on the data used to make the 
dictionariesor something.
So I was happy to see Marco suggesting a function alongthe lines of my thought 
process. But I have another  thought.A stand-alone function along his lines 
might be fine. Buta method built into a general Dictionary class is 
anotherthing as it asks a method in one dictionary to march aroundinto other 
dictionaries. So I wonder if a better methodis sort of recursive. 
If you had a class like dictionary that had a getdeep function,and it got 
called with N arguments, and perhaps a namedargument supplying a default, then 
would it make sensefor the function checking to see if the FIRST argument canbe 
found as a key to the current dictionary. 
If arguments remain then it should expect to finda result that is a dictionary 
(or perhaps some otherobject that supports the getdeep() protocol and ask 
thatobject to operate on the N-1 remaining arguments, passingthe default along 
too.
If the request is valid, after some iterations an object willhave a method 
invoked with a single argument (plus default)and a value passed back up the 
chain. For any errors alongthe way, the default would be returned.
Is this closer to the spirit of the request? I view this versionof nested 
dictionaries as a sort of tree structure with variablebranches along the way. 
So an approach like this could makesense and perhaps Python could be updated 
eventually to havesome objects support such a protocol.
Of course you could sort of do it yourself by subclassing somethingand making 
changes but that may not work for what is already asort of built-in data 
structure but could work for one of many variantsalready implemented in modules.



-----Original Message-----
From: Marco Sulla <marco.sulla.pyt...@gmail.com>
To: Peter J. Holzer <hjp-pyt...@hjp.at>
Cc: python-list@python.org
Sent: Sun, Apr 3, 2022 5:17 pm
Subject: Re: dict.get_deep()

On Sun, 3 Apr 2022 at 21:46, Peter J. Holzer <hjp-pyt...@hjp.at> wrote:
>
> > > data.get_deep("users", 0, "address", "street", default="second star")
>
> Yep. Did that, too. Plus pass the final result through a function before
> returning it.

I didn't understand. Have you added a func parameter?

> I'm not sure whether I considered this when I wrote it, but a function
> has the advantage of working with every class which can be indexed. A
> method must be implemented on any class (so at least dict and list to be
> useful).

You're right, but where to put it? I don't know if an iterableutil package
exists. If included in the stdlib, I don't know where to put it. In
collections maybe?

PS: if you're interested, here is my implementation:

def get_deep(self, *args, default=_sentinel):
    r"""
    Get a nested element of the dictionary.

    The method accepts multiple arguments or a single one. If a single
    argument is passed, it must be an iterable. This represents the
    keys or indexes of the nested element.

    The method first tries to get the value v1 of the dict using the
    first key. If it finds v1 and there's no other key, v1 is
    returned. Otherwise, the method tries to retrieve the value from v1
    associated with the second key/index, and so on.

    If in any point, for any reason, the value can't be retrieved, the
    `default` parameter is returned if specified. Otherwise, a
    KeyError or an IndexError is raised.
    """

    if len(args) == 1:
        single = True

        it_tpm = args[0]

        try:
            len(it_tpm)
            it = it_tpm
        except Exception:
            # maybe it's a generator
            try:
                it = tuple(it_tpm)
            except Exception:
                err = (
                    f"`{self.get_deep.__name__}` called with a single " +
                    "argument supports only iterables"
                )

                raise TypeError(err) from None
    else:
        it = args
        single = False

    if not it:
        if single:
            raise ValueError(
                f"`{self.get_deep.__name__}` argument is empty"
            )
        else:
            raise TypeError(
                f"`{self.get_deep.__name__}` expects at least one argument"
            )

    obj = self

    for k in it:
        try:
            obj = obj[k]
        except (KeyError, IndexError) as e:
            if default is _sentinel:
                raise e from None

            return default

    return obj
-- 
https://mail.python.org/mailman/listinfo/python-list
-- 
https://mail.python.org/mailman/listinfo/python-list

Reply via email to