Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Python Collections (Retired) Dictionaries Teacher Stats

Elizabeth McInerney
Elizabeth McInerney
3,175 Points

Python collections/dictionaries/teacher assignment

When bottom 9 lines of this code runs, it replaces every teacher in the list with the last teacher in the list (along with his number of classes). I don't understand why it does not append.

teachers.py
# The dictionary will be something like:
# {'Jason Seifer': ['Ruby Foundations', 'Ruby on Rails Forms', 'Technology Foundations'],
#  'Kenneth Love': ['Python Basics', 'Python Collections']}
#
# Often, it's a good idea to hold onto a max_count variable.
# Update it when you find a teacher with more classes than
# the current count. Better hold onto the teacher name somewhere
# too!
#
# Your code goes below here.
# The dictionary will be something like:

my_dict = {'Jason Seifer': ['Ruby Foundations', 'Ruby on Rails Forms', 'Technology Foundations'],
'Kenneth Love': ['Python Basics', 'Python Collections']}

#
# Often, it's a good idea to hold onto a max_count variable.
# Update it when you find a teacher with more classes than
# the current count. Better hold onto the teacher name somewhere
# too!
#
# Your code goes below here.

def most_classes(my_dict):
  largest = 0
  for key in my_dict:
    if len(my_dict[key]) > largest:
      largest = len(my_dict[key])
      busy_teacher = key
  print(busy_teacher)
  return busy_teacher  
most_classes(my_dict)


def num_teachers(my_dict):
  number = 0
  for key in my_dict:
    number += 1
  return number
num_teachers(my_dict)

# looking to create a list of lists in the format [<name>,<number of classes>]
my_list = ['name',0]
list_of_lists = []
def stats(my_dict):
  for key in my_dict:
    my_list[0] = key
    my_list[1] = len(my_dict[key])
    list_of_lists.append(my_list)
  return list_of_lists  
stats(my_dict)

4 Answers

William Li
PLUS
William Li
Courses Plus Student 26,868 Points

Hi, Elizabeth McInerney you don't need the my_list variable as a placeholder for the temp values. You could simplify the code this way.

def stats(my_dict):
  result = []
  for key in my_dict:
    result.append([key, len(my_dict[key])])
  return result

This would work.


Now, as to why your code isn't working like it supposed to. It's because of these 3 lines.

    my_list[0] = key
    my_list[1] = len(my_dict[key])
    list_of_lists.append(my_list)

Here, it actually brings up a VERY important topic in Python. Pass by Reference VS Pass by Value.

On each iteration of the dictionary key, the 0, and 1st index of my_list values got reassigned, then my_list was appended to the list_of_lists. The final result of the list_of_lists looks sth like this. [[key1, number1], [key2, number2], [key3, number3] ...].

The key1, key2, key3, number1, number2, number3 here aren't holding the actual values, they all reference to whatever values key and len(my_dict[key] point to.

And since the values of key and len(my_dict[key] change each time it iterates through the key in the dictionary, the values that key1, key2, key3, number1, number2, number3 reference to change accordingly. Therefore when the iteration is over,

it replaces every teacher in the list with the last teacher in the list (along with his number of classes).

Perhaps it'd help you see it better if we look at a simpler examples here.

li = []
a = [1,2,3]
b = a        # assign a to b
a[0] = 10    # reassign the 0th index value of a
print(a)     # => [10, 2, 3]
print(b)     # => [10, 2, 3]
li.append(a)
li.append(b)
print(li)    # => [[10,2,3],[10,2,3]]
a[0] = 4
print(li)    # => [[4,2,3],[4,2,3]]

The b list changes even though we only modified the a list, that's because both a and b point to the same list, you change one, change the other one.

One last thing, In your original code

# looking to create a list of lists in the format [<name>,<number of classes>]
my_list = ['name',0]
list_of_lists = []
def stats(my_dict):
  for key in my_dict:
    my_list[0] = key
    my_list[1] = len(my_dict[key])
    list_of_lists.append(my_list)
  return list_of_lists  
stats(my_dict)

If you wanna make it works, make one small change in this line list_of_lists.append(my_list)

list_of_lists.append(my_list[:])

By doing so, you're appending a copy of my_list into list_of_lists instead of just a reference to the list.

To learn more about Pass by Reference VS Pass by Value in Python, you can check out these 2 articles:

Hope this helps.

Elizabeth McInerney
Elizabeth McInerney
3,175 Points

Wow, I had no idea. Have we learned that yet?

Elizabeth McInerney
Elizabeth McInerney
3,175 Points

I see that your change works, but I don't understand why append(my_list) appends something that has pointers that change with each loop through the function, while append(my_list[:]) appends something that is fixed.

The article "Is Python pass-by-reference or pass-by-value was really helpful.

William Li
William Li
Courses Plus Student 26,868 Points

Hello, Elizabeth

I'm sure at this point you're familiar with slicing list with index in Python, right?

When you

  1. list[1:3], you slice out the 2nd and 3rd items from the list
  2. list[:-1], you can leave the beginning index empty, here you slice out every items except the last one.
  3. list[1:], same logic, by leaving the end index out, you slice out every items in the list but the 1st one here.

You can also leave out both the beginning and end index like this list[:], that way the thing you slice out is the exact same list as the original one.

Now, the important thing here is that, in Python, the result that comes out from list slicing is actually a new list object in memory. That's why the notion of list[:] is used very frequently in Python to make a copy of the list.

a = [1,2,3]
b = a[:]       # make a copy of a, then assign to b

In the above code, you can safely change the values in b list without effecting a, and vise versa. But please remember this, it doesn't change the fact that b variable is only serves as pointer to a list object, that's just the nature of list assignment in Python, only this time it points to a list that's identical to a, but existed independent to a.

Typed this out in a rush, hope there isn't much error here, also hope that it helps you understand about the topic a little better.

Elizabeth McInerney
Elizabeth McInerney
3,175 Points

Wow, that's fascinating. I don't remember seeing that point raised in any of the videos, but I will remember it now.

So I see also that I was also getting confused by the :, when you see that symbol between {}, it is used in a dictionary to separates the key/value pairs, when you see it between [], it is slicing a list.

William Li
William Li
Courses Plus Student 26,868 Points

Yep, symbols have different meaning depending on how they were used. For example: the + symbol, when you put it between 2 numbers, it performs addition; but when you put it between 2 Strings, it combines them together.

Also, when writing code in Python, it's important to keep in mind the distinction between pass-by-reference VS pass-by-value; it's best practice not to assign one list to too many variables, because that creates a chain of dependency, you make change in one place, multiple values are effected without your knowledge, this kind of bug can be very hard to trace.