beeboop
beeboop

Reputation: 31

Way to execute a function on each getting of dictionary value

I'd like to define a dictionary to use as a template and upon getting of the value, have it execute a function

d = {'choice': random.choice([1, 2, 3, 4, 5])

>>> d.get('choice')
3
>>> d.get('choice')
1
>>> d.get('choice')
5

Ultimately, I'd like to have a list of these dictionary templates that I would loop over, and each time the dictionary is iterated upon, for certain fields (like choice above) I would like to have a random value out of the list

Upvotes: 2

Views: 55

Answers (3)

martineau
martineau

Reputation: 123491

You can subclass the built-in dict class and specialize it by overriding its __getitem__() method to do what's needed.

In the example code below the function to call, arguments to pass it, and an id are all passed to the class initializer when an instance of the class is created — making it very generic because you can create separate instances that will call a different function (or passing the same one a different set of arguments or give it a different id).

Instance of the derived class can be otherwise be initialized and used like a normal dictionary.

class FuncDict(dict):
    def __init__(self, *args, func_info=None, **kwargs):
        if not func_info or len(func_info) != 3:
            raise RuntimeError(
                    'A "func_info" keyword argument of three elements is required')
        super().__init__(*args, **kwargs)  # Initialize base class instance.
        self.func, self.func_args, self.func_id = func_info  # Save.

    def __getitem__(self, key):
        return (self.func(self.func_args) if key == self.func_id
                else super().__getitem__(key))


if __name__ == '__main__':

    import random

#    fd = FuncDict([('foobar', 42)], func_info=(random.choice, [1,2,3,4,5], 'choice'))
    fd = FuncDict(foobar=42, func_info=(random.choice, [1,2,3,4,5], 'choice'))

    print(fd['choice'])  # -> Some value from 1 to 5.
    print(fd['foobar']) # -> 42

Upvotes: 0

user15270287
user15270287

Reputation: 1153

One other thing you could do is just have the function be the value in the dictionary. So it would look like:

l = [1, 2, 3]
d = {'choice': random.choice}

and then to actually get the number you can say d['choice'](l). If you need more flexibility you can define your own lambda/wrapper functions.

Upvotes: 0

SuperStormer
SuperStormer

Reputation: 5397

Create a new class that inherits from dict, then override __getitem__. For instance:

import random

class MyDict(dict):
    def __getitem__(self, key):
        if key == "choice":
            return random.choice(super().__getitem__(key))
        else:
            return super().__getitem__(key)

d = MyDict(choice=[1,2,3,4,5],something_else=1)

print(d["choice"]) # outputs a random value from 1 to 5
print(d["something_else"]) # still outputs 1

This can be extended to use a second dict of templates used for custom behavior in __getitem__ depending on the key.

Upvotes: 2

Related Questions