0

I'm trying to add new keys to the existing dictionary, setting as default value, the value of the previous key.

def check_data(end, start, profile):
    print(profile)

    for day in range((end-start).days+1):
        profile.setdefault(start+datetime.timedelta(day),
           {'expediture':0, 'top-up':0,
           'budget':profile[start+datetime.timedelta(day-1)]['budget'] })

start  = datetime.date(2019,2,1)
end    = datetime.date(2019,2,17)
check_data(end, start, month_data)

output from print(profile):

    {datetime.date(2019, 2, 1): {'expediture': 0, 'top-up': 0, 'budget': 100.0}}

 ---------------------------------------------------------------------------
    KeyError                                  Traceback (most recent call last)
    <ipython-input-4-e44462627692> in <module>
    ----> 1 check_data(end, start, month_data)

    <ipython-input-2-90b936150b04> in check_data(end, start, profile)
         20         profile.setdefault(start+datetime.timedelta(day),
         21                                     {'expediture':0, 'top-up':0,
    ---> 22
       'budget':profile[start+datetime.timedelta(day-1)]['budget'] })
        23
        24 def add_money(profile, topup, date=datetime.date.today()):

       KeyError: datetime.date(2019, 1, 31)

I don't understand why setdefault() tries to set default value to datetime.date(2019, 2, 1) if this value already exists.

I could fix this problem with if but I'd like to understand how setdefault works and maybe there is alternative solution for this problem.

martineau
  • 119,623
  • 25
  • 170
  • 301
Artur
  • 21
  • 1
  • 6
  • The second argument to `setdefault` is evaluated before `setdefault` is actually run. – chepner Feb 15 '19 at 17:48
  • 4
    ALL parameters to a function are completely evaluated before the function is called - whether or not that function is actually going to make use of a particular parameter. So in this case, you're creating the expenditure/top-up/budget dict unconditionally, even on the first day in which case the budget calculation fails due to lack of a previous day. – jasonharper Feb 15 '19 at 17:49
  • Thanks for explanation! – Artur Feb 15 '19 at 18:19

2 Answers2

1

You are misinterpreting the error. A KeyError means you tried to look up a key in a dictionary but that key does not exist. In this case it looks like profile[start+datetime.timedelta(day-1)] is giving you the KeyError and is not related to the setdefault method call.

liamhawkins
  • 1,301
  • 2
  • 12
  • 30
0

Consider how you would write this code without setdefault (and some minor refactoring for readability):

def check_data(end, start, profile):
    print(profile)

    for day in range((end-start).days+1):
        yesterday = start + datetime.timedelta(day - 1)
        today = start + datetime.timedelta(day)
        profile[today] = {
            'expenditure': 0,
            'top-up': 0,
            'budget': profile[yesterday]['budget']
        }

start  = datetime.date(2019,2,1)
end    = datetime.date(2019,2,17)
check_data(end, start, month_data)

On the first iteration of the loop, you need the value of profile[datetime(2019,1,31)] in order to set the value of profile[datetime(2019,2,1)], but that value was never set.

The only use for setdefault here would be with the key yesterday, assuming you could start with some generic profile. For simplicity, I'll assume that budget is also an integer.

def check_data(end, start, profile):
    print(profile)

    for day in range((end-start).days+1):
        yesterday = start + datetime.timedelta(day - 1)
        today = start + datetime.timedelta(day)
        profile[today] = {
            'expenditure': 0,
            'top-up': 0,
            'budget': profile.setdefault(yesterday, {'budget': 0})['budget']
        }

Now, if profile[yesterday] doesn't already exist, setdefault will execute the equivalent of profile[yesterday] = {'budget': 0} before returning that value.

chepner
  • 497,756
  • 71
  • 530
  • 681