Pekka
Pekka

Reputation: 2448

Temporarily unpack dictionary

Say, I have a dic like this

my_dictionary = {'a':1,'c':5,'b':20,'d':7}

Now, I want to do this with my dic:

if my_dictionary['a'] == 1 and my_dictionary['d'] == 7:
    print my_dictionary['c']

This looks ridiculous because I am typing my_dictionary 3 times!

So is there any syntax which would allow me to do something like this:

within my_dictionary:
    if a == 1 and d == 7:
        print c

This would actually work if I didn't have anything more (in this case b) in my dic:

def f(a,d,c):
    if a == 1 and d == 7:
        print c 

f(**my_dictionary)

Upvotes: 3

Views: 127

Answers (3)

muddyfish
muddyfish

Reputation: 3650

To answer

So is there any syntax which would allow me to do something like this:

within my_dictionary:
if a == 1 and d == 7:
    print c

You can subclass dict so that it has with magic methods. To do this, the class must have __enter__ and __exit__ methods. You could then export the keys to the local scope of the with statement and clean them up with the exit method.

Using this answer I was able to create a subclass that did so:

import inspect
import ctypes

locals_to_fast = ctypes.pythonapi.PyFrame_LocalsToFast
locals_to_fast.restype = None
locals_to_fast.argtypes = [ctypes.py_object, ctypes.c_int]

class WithDict(dict):
    def __enter__(self):
        frame = self.get_frame()
        for k,v in self.iteritems():
            frame.f_locals[str(k)] = v
            locals_to_fast(frame, 1)

    def __exit__(self, exc_type, exc_value, traceback):
        frame = self.get_frame()
        for k in self.keys():
            del frame.f_locals[str(k)]

    def get_frame(self):
        return inspect.getouterframes(inspect.currentframe())[2][0]

A test case using your original example

my_dictionary = WithDict({'a':1,'c':5,'b':20,'d':7})
with my_dictionary:
    if a == 1 and d == 7:
        print c

prints 5

The variables get deleted when the with statement completes

Upvotes: 2

Kasravnd
Kasravnd

Reputation: 107297

You can use operator.itemgetter to minimize multiple indexing :

>>> if operator.itemgetter('a','d')(my_dictionary)==(1,7):
...      print operator.itemgetter('c')(my_dictionary)

And you can use it in a function :

>>> def get_item(*args):
...   return operator.itemgetter(*args)(my_dictionary)
... 
>>> 
>>> if get_item('a','d')==(1,7):
...      print get_item('c')
... 
5

Upvotes: 3

yangjie
yangjie

Reputation: 6725

You can change your function to

def f(a,d,c,**args):
    if a == 1 and d == 7:
        print c

then it will work even if you have other items in the dict.

Upvotes: 4

Related Questions