0

dictionary extraction normal method

d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
print(d['a'])  # prints: 1

requirement:

print(d['a', 'b'])  # should print: [1, 2]

will this be possible by subclassing dict and modifying __getitem__() method, if possible, how?

ruohola
  • 21,987
  • 6
  • 62
  • 97
VISHBALA
  • 87
  • 1
  • 1
  • 5
  • 2
    could also always just write this requirement as a function instead of messing with a perfectly normal datatype. something like `fetch(dict, key_list)` – Paritosh Singh Apr 03 '19 at 14:17
  • 3
    Yes, it's possible. Have you tried this yet, did you get stuck anywhere? – Martijn Pieters Apr 03 '19 at 14:17
  • I would advice against subclassing built-ins and altering their behavior, will be very confusing for others when you're using invented syntax with seemingly ordinary dictionaries. Would be best to just make a function like @ParitoshSingh suggested. – ruohola Apr 03 '19 at 14:25
  • 1
    @ruohola: subclassing is *fine*, as long as you don't replace all `dict` objects. Loads of good Python projects subclass `dict`, including the standard library (`collections.defaultdict` and `collections.Counter` both are such subclasses). Python is not Ruby, luckily. – Martijn Pieters Apr 03 '19 at 14:26
  • @MartijnPieters very true, this just screams that OP wants to be able to do this with any random dictionary... – ruohola Apr 03 '19 at 14:29
  • `operator.itemgetter` implements something like this. – hpaulj Apr 03 '19 at 14:29
  • @ruohola: creating a new instance from an existing dictionary is not a problem either though, e.g. `d = MultiKeyDict(d)`. – Martijn Pieters Apr 03 '19 at 14:30
  • @hpaulj: that inverts the operation, though. `itemgetter('a', 'b')(d)` is not all that readable, in this specific scenario. Then just stick with `[d[k] for k in ('a', 'b')]`.. – Martijn Pieters Apr 03 '19 at 14:31
  • @MartijnPieters i have no idea how dict.__getitem__() method looks like, how can i see its code. – VISHBALA Apr 03 '19 at 14:42

2 Answers2

0

If you really want to use subclassing, this will do the job quite nicely:

class CustomDict(dict):
    def __init__(self, dic):
        self.dic = dic

    def __getitem__(self, items):
        values = []
        for item in items:
            values.append(self.dic[item])
        return values if len(values) > 1 else values[0]


d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}

new_d = CustomDict(d)

print(new_d['a'])
print(new_d['a', 'b'])
print(new_d['a', 'b', 'c'])
print(new_d['a', 'c', 'd'])

Output:

1
[1, 2]
[1, 2, 3]
[1, 3, 4]

Explanation:

The new_d is an object of the CustomDict class, it will always fall back to the parent class's methods (so that looping over the object and other things you might want to do to a dictionary work) except when one of the overriden methods (init, getitem) get called.

So when one uses new_d['a', 'b'] the overriden __getitem__ method gets called. The overriden method uses the __getitem__ of the self.dic (which is a normal dictionary) to actually access the dictionary values for every key given.

ruohola
  • 21,987
  • 6
  • 62
  • 97
-2
def ret_multiple(dict, *args)
    for k in args:
        if k in dict:
            ret.append(dict[k])
    return ret

vals = ret_multiple(d, 'a', 'b')   
amchugh89
  • 1,276
  • 1
  • 14
  • 33
  • 1
    Why use a mutable default value for `keys` at all? At the very least make it a `()` tuple so it can't be accidentally modified. And you could just use a `*keys` catch-all argument and so support `ret_multiple(d, 'a', 'b')`. – Martijn Pieters Apr 03 '19 at 14:29
  • 1
    Idk dude I just wrote this super fast. And I'm not that smart – amchugh89 Apr 03 '19 at 14:29
  • 1
    @amchugh89 you could initialize `ret` however, not use the built-in `dict` and add `:` on the function definition line. Maybe superfast was *way too fast*. – Ma0 Apr 03 '19 at 14:53
  • Even though this may answer the question, there is no explanation of your code. Please update your answer to provide an explanation of what you are doing. Thanks! – Miroslav Glamuzina Apr 04 '19 at 02:41