0

In a class Week, I have created a dictionary using the .fromkey() method that contains a string key for Monday-Saturday and initializes them all to an empty list:

self.weekDict = dict.fromkeys(dayNameList, [])

I have a function that then iterates over a list of employees, and, within each iteration, iterates over their work days and appends an employee to the corresponding weekDict list of each day:

def setWeekDict(employees):
            for employee in employees:
                for day in employees[employee].workDays:
                    self.weekDict[day].append(employee)
                    print "employee: " + employee
                    print "weekday/list: " + str(day) + str(self.weekDict[day])

I expect the weekDict lists to reflect everyone who is scheduled to work on a given day. Instead, it appears that the list for each day accumulates the total number of times I've iterated over each day for the employee. My print statements output this:

employee: Addison
weekday/list: Saturday['Addison']
employee: Addison
weekday/list: Monday['Addison', 'Addison']
employee: Addison
weekday/list: Tuesday['Addison', 'Addison', 'Addison']
employee: Addison
weekday/list: Wednesday['Addison', 'Addison', 'Addison', 'Addison']
employee: Addison
weekday/list: Thursday['Addison', 'Addison', 'Addison', 'Addison', 'Addison']

Obviously I only want 'Addison' or any other employee to appear once per day, and I can't understand what in my for loop would be contributing to each list adding the employee name + the amount of times the employee name has been added to other lists before. I can only think that dict.fromkeys() creates keys that actually all point to the same list, but I didn't think that was how the fromkeys() function worked.

user1427661
  • 11,158
  • 28
  • 90
  • 132
  • 3
    You didn't read my answer carefully : http://stackoverflow.com/a/14259052/846892 – Ashwini Chaudhary Jan 10 '13 at 19:37
  • Haha, you must have updated it since I last saw the post. Thanks. So fromkeys() only copies a reference when a mutable object is used -- good to know! – user1427661 Jan 10 '13 at 19:42
  • 1
    @user1427661 exactly, it copies the same object over and over. – Ashwini Chaudhary Jan 10 '13 at 19:44
  • 2
    @user1427661: `fromkeys()` only uses the same object even if an immutable object is used. It's simply that you can't get yourself into as much trouble that way because the object is immutable. – DSM Jan 10 '13 at 19:47

1 Answers1

2

fromkeys() if used with mutable objects can lead to such behavior because it assigns the same object(same id()) to every key of the dictionary.

In [16]: dic=dict.fromkeys("spam",[])

In [17]: [id(x) for x in dic.values()]    #all of them are same objects
Out[17]: [141885196, 141885196, 141885196, 141885196]

In [18]: dic['s'].append(1)   #changing one changes others as well

In [19]: dic
Out[19]: {'a': [1], 'm': [1], 'p': [1], 's': [1]}

So, dict-comprehensions must be used with mutable objects:

In [20]: dic={x:[] for x in "spam"}

In [21]: [id(x) for x in dic.values()]               #unique objects
Out[21]: [141884972, 141848300, 142262988, 141895980]

In [22]: dic['s'].append(1)

In [23]: dic
Out[23]: {'a': [], 'm': [], 'p': [], 's': [1]}
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • If you need to support Python 2.6 or before, note that dictionary comprehensions were [added in 2.7](http://www.python.org/dev/peps/pep-0274/). You could make a similar generator expression and pass it to the `dict` constructor - `dic = dict((x, []) for x in dayNameList)` or use a [`defaultdict`](http://docs.python.org/2/library/collections.html#collections.defaultdict) (added in 2.5) - `from collections import defaultdic; dic = defaultdict(list)`. – Blair Jan 10 '13 at 19:56