Bring line continuation to multi-level dictionary lookup

Hi everyone. I work with APIs which have deep nested dictionary structure response. Imagine a simplified case: foo = {1: {2: {3: {4: {5: 6 } } } } Now imagine I need to get to 6: foo['1']['2']['3']['4']['5']['6'] This looks managable, but if the key name is long, then I certainly will end doing this to respect my style guide. To make it concrete, let's use something reallistic, a response call from AWS API: response = {'DescribeDBSnapshotsResponse': {'ResponseMetadata': {'RequestId': '123456'}, 'DescribeDBSnapshotsResult': {'Marker': None, 'DBSnapshots': [{'Engine': 'postgres'}]}}} If I had to get to the Engine I'd do this: detail_response = response["DescribeDBSnapshotsResponse"] result = detail_response["DescribeDBSnapshotsResult"] This is only a few level deep, but imagine something slightly longer (I strict out so much from this response). Obviously I am picking some real example but key name being really long to sell my request. Can we do it differently? How about print(response.get( "DescribeDBSnapshotsResponse").get( "DescribeDBSnapshotsResult").get( "DBSnapshots")[0].get( "Engine")) Okay. Not bad, almost like writing in Javascript except Python doesn't allow you to do line continuation before the got at all, so you are stuck with (. But the problem with the alternative is that if DescribeDBSnapshotsResult is a non-existent key, you will just get None, because that's the beauty of the .get method for a dictionary object. So while this allows you to write in slightly different way, I am costing silent KeyError exception. I wouldn't know which key raised the exception. Whereas with [key1][key2] I know if key1 doesn't exist, the exception will explain to me that key1 does not exist. So here I am, thinking, what if we can do this? response( ["DescribeDBSnapshotsResponse"] ["DescribeDBSnapshotsResult"] ) You get the point. This looks kinda ugly, but it doesn't require so many assignment. I think this is doable, after all [ ] is still a method call with the key name passed in. I am not familar with grammar, so I don't know how hard and how much the implementation has to change to adopt this. Let me know if this is a +1 or -10000000 bad crazy idea. Thanks. John

John Wong <gokoproject@gmail.com> writes:
import functools import operator functools.reduce(operator.getitem, [ "DescribeDBSnapshotsResponse", "DescribeDBSnapshotsResult", "DBSnapshots", 0, "Engine"], response)

On Wed, Sep 16, 2015 at 11:07 PM, John Wong <gokoproject@gmail.com> wrote:
I think a much better idea is to create a utility function: def dig(container, *path): obj = container for p in path: obj = obj[p] return obj Then you can do your long lookup like so: engine = dig(response, 'DescribeDBSnapshotsResponse', 'DescribeDBSnapshotsResult', 'DBSnapshots', 0, 'Engine') This came up on python-list a month or two ago; perhaps there's some merit in finding a place to stick this utility function. -- Zach

On 2015-09-16 21:07, John Wong wrote:
You can just use the existing parentheses-based line continuation for this: (foo[1] [2] [3] [4] [5] ) -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown

On Sep 16, 2015, at 21:07, John Wong <gokoproject@gmail.com> wrote:
This already has a perfectly valid meaning: you have a list of one string, you're indexing it with another string, and passing the result to a function. If this isn't obvious, try this example: frobulate(['a', 'e', 'i', 'o', 'u'][vowel]) So, giving it a second meaning would be ambiguous. Also, there's already a perfectly good way to write what you want. (Actually two, because square brackets continue the exact same way parens do, but I wouldn't recommend that here.) (response ["DescribeDBSnapshotsResponse"] ["DescribeDBSnapshotsResult"] ) That looks no uglier than your suggestion, and a lot less ugly when buried inside a larger expression. (I think it might look nicer to indent the bracketed keys, but I think that technically violates PEP 8.)

On Thu, Sep 17, 2015 at 2:06 AM, Andrew Barnert <abarnert@yahoo.com> wrote:
Great catch... I did not even consider this. You are right.
Thank you all. I think I should have noticed (response[..]) would work...

John Wong <gokoproject@gmail.com> writes:
import functools import operator functools.reduce(operator.getitem, [ "DescribeDBSnapshotsResponse", "DescribeDBSnapshotsResult", "DBSnapshots", 0, "Engine"], response)

On Wed, Sep 16, 2015 at 11:07 PM, John Wong <gokoproject@gmail.com> wrote:
I think a much better idea is to create a utility function: def dig(container, *path): obj = container for p in path: obj = obj[p] return obj Then you can do your long lookup like so: engine = dig(response, 'DescribeDBSnapshotsResponse', 'DescribeDBSnapshotsResult', 'DBSnapshots', 0, 'Engine') This came up on python-list a month or two ago; perhaps there's some merit in finding a place to stick this utility function. -- Zach

On 2015-09-16 21:07, John Wong wrote:
You can just use the existing parentheses-based line continuation for this: (foo[1] [2] [3] [4] [5] ) -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown

On Sep 16, 2015, at 21:07, John Wong <gokoproject@gmail.com> wrote:
This already has a perfectly valid meaning: you have a list of one string, you're indexing it with another string, and passing the result to a function. If this isn't obvious, try this example: frobulate(['a', 'e', 'i', 'o', 'u'][vowel]) So, giving it a second meaning would be ambiguous. Also, there's already a perfectly good way to write what you want. (Actually two, because square brackets continue the exact same way parens do, but I wouldn't recommend that here.) (response ["DescribeDBSnapshotsResponse"] ["DescribeDBSnapshotsResult"] ) That looks no uglier than your suggestion, and a lot less ugly when buried inside a larger expression. (I think it might look nicer to indent the bracketed keys, but I think that technically violates PEP 8.)

On Thu, Sep 17, 2015 at 2:06 AM, Andrew Barnert <abarnert@yahoo.com> wrote:
Great catch... I did not even consider this. You are right.
Thank you all. I think I should have noticed (response[..]) would work...
participants (5)
-
Akira Li
-
Andrew Barnert
-
Brendan Barnwell
-
John Wong
-
Zachary Ware