1

Given a dictionary that is consistently referred to :

def getMyDict():
    myDict = {};
    myDict['1'] = '1'
    return myDict

To access a dictionary value I use :

getMyDict()['1']

How to design dictionary so that the creation of the dictionary is just evaluated once. So that multiple calls to getMyDict['1'] do not result in multiple calls to myDict['1'] = '1'

Coming from Scala this can be accomplished using lazy keyword : What does a lazy val do?

This functionality does not appear to exist in Python as is ?

Closest i found is : Python lazy evaluator

The above dictionary example is not real world, but contrived to illustrate the point.

blue-sky
  • 51,962
  • 152
  • 427
  • 752
  • It's not clear what you want to be lazily evaluated here. The only thing in that dictionary is a string, which does not need evaluation. – Daniel Roseman Oct 20 '17 at 08:07
  • Not sure what you mean. Which bit should be lazy? Is it something that is expensive to construct so that you only want to do it once if needed or not at all if never really asked for? – quamrana Oct 20 '17 at 08:10
  • @quamrana please see update, hope this makes it clearer. – blue-sky Oct 20 '17 at 08:21
  • 3
    Once the key has been added, `myDict['1']` does not result in multiple calls to `myDict['1'] = '1'`. Why should it? Moreover, in your example you can add the key at dictionary initialisation: `myDict = {"1": "1"}`; – Eli Korvigo Oct 20 '17 at 08:23
  • No, this does not make it any clearer. Maybe instead of a "contrived" example you should show what you actually want to do. – Daniel Roseman Oct 20 '17 at 08:30
  • @EliKorvigo please see question update. – blue-sky Oct 20 '17 at 08:32
  • 3
    Now you've written invalid code. `getMyDict` is a function, so you would need to do `getMyDict()['1']`. But *why* would you do this? If you have a function that returns a dict, assign that return value to a variable. – Daniel Roseman Oct 20 '17 at 08:32
  • Why do you want to create a dictionary anew every time you call `getMyDict`? If you want to encapsulate the dict, you can use a closure. Is that what you want? – Eli Korvigo Oct 20 '17 at 08:37

3 Answers3

2

I also cannot understand what you mean, but I try to understand. To make it clearer:

def Ops():
    print("Ops")

lazy_dict = {}
lazy_dict["Ops"] = Ops()  # not actually evaluate
...
print(lazy_dict["Ops"])   # run here

Do you mean something like the above? When bind a value to dict's key, the key just keep a lazy reference to this value instead of the real value.

But it is still confused as is this a behave of dict or a behave of value?

For example:

There is a lib which implement a lazy wrapper for functions, if you bind a lazy function to dict's key, it will exactly be a lazy evaluation:

import lazy

@lazy
def Ops():
    print("Ops")

lazy_dict = {}
lazy_dict["Ops"] = Ops()  # not actually evaluate
...
print(lazy_dict["Ops"])   # run here
Sraw
  • 18,892
  • 11
  • 54
  • 87
  • Do you understand the logic of the code of that lib? Or do you have documentation about it? I would like to understand how it works underhood – Eric Bellet Jun 17 '19 at 09:51
2

You could use a modified version (below) of the run_once decorator described in this answer.

def run_once(f):
    def wrapper(*args, **kwargs):
        if not wrapper.has_run:
             wrapper.has_run = True
             wrapper.cache = f(*args, **kwargs)
        return wrapper.cache
    wrapper.has_run = False
    return wrapper

@run_once
def getMyDict():
    myDict = {}
    myDict['1'] = '1'
    print('Run getMyDict')
    return myDict


print(getMyDict()['1'])
print(getMyDict()['1'])
print(getMyDict()['1'])

Output:

Run getMyDict
1
1
1

I am assuming here that your real getMyDict() function is a really heavyweight function. Such a function might generate a huge dictionary, taking information from disk or off the internet etc etc.

Update:

This technique also works:

One improvement would be to rename this function to describe its real purpose:

def initMyDict():
    myDict = {}
    myDict['1'] = '1'
    print('Run initMyDict')
    return myDict

Now the name of the function implies that it should be run once only.

Now let's write the function you want to call:

def getMyDict(myDict=initMyDict()):
    return myDict

and let's call it:

print(getMyDict()['1'])
print(getMyDict()['1'])
print(getMyDict()['1'])

Output:

Run initMyDict
1
1
1

This technique does have two flaws:

  1. The dict is initialised everytime the program runs
  2. The call site returns the parameter if you provide one.
quamrana
  • 37,849
  • 12
  • 53
  • 71
1

what i'm comprehending from the question is that you want to make a function to update a dict (and return it as well) but though you've hard-coded myDict['1'] = '1'

you are wondering how to put other values in it and not call myDict['1'] = '1' again and again.

in my opinion what you can you do is pass the values in the function.

def getMydict(key=x, val=y):
    tempDict = {key: val}
    return tempDict

tempDict has only scope inside the function , so if you want to update a existing dict you can pass the dictionary name as well like.

def getMydict(dictname=stuff, key=x, val=y):
    dictname.update({key: val})
    return dictname

hope that helps.

P.hunter
  • 1,345
  • 2
  • 21
  • 45