3

I have a nested list and I want to make a function to convert times (with the specific format "XX:XX") into the string "time". I'm curious as to why my first example works but not my second. Why must I clone for each column to save the list comp's result? Lists are mutable, so shouldn't I be able to just save the row's result in place?

Doesn't work

def timeConvert2(schedule):
    for eachClass in schedule:
        eachClass = ["time" if x[2] == ':' else x for x in eachClass]
        return schedule

timeConvert([["abc", "09:09", "10:10"], ["def", "11:11", "12:12"]])

=> [["abc", "09:09", "10:10"], ["def", "11:11", "12:12"]]

Works

def timeConvert1(schedule):
    for eachClass in schedule:
        eachClass[:] = ["time" if x[2] == ':' else x for x in eachClass]
        return schedule

timeConvert([["abc", "09:09", "10:10"], ["def", "11:11", "12:12"]])

=> [["abc", "time", "time"], ["def", "time", "time"]]

I'm expecting the first example I gave to work, but the original list isn't changing...

Community
  • 1
  • 1

3 Answers3

3

In the first version you create a new list and change the eachclass to point to the new list. You don't change in schedule anything since it still contains the pointer to the old list.

In the second version you use slice assignment. This causes the content of the old list to be replaced with the content of the new list.

You should checkout this question which explains how slice assignment works.

Michal Yanko
  • 389
  • 1
  • 6
1

The difference is, with an example:

  • eachClass = X creates a local variable eachClass, and assigns X to it
  • eachClass[:] = X slices into the element of the original nested list schedule, and assigns the contents of X to that slice
list_a = [1,2,3]

for a in list_a:
    a = 5
    print(list_a)
>> [1,2,3]
>> [1,2,3]
>> [1,2,3]
list_a[0:2] = [5,5]
print(list_a)
>> [5,5,3]
OverLordGoldDragon
  • 1
  • 9
  • 53
  • 101
1

In your timeConvert1 you are doing a slice assignment which will make modifications to the original list so when you return schedule you see the necessary changes applied on it. In your timeConvert2 you are creating a new list but returning the old unmodified list so you don't see necessary changes in it. I have added a small mode snippet below to illustrate this

def timeConvert2(schedule):
    for eachClass in schedule:
        print("ID before: {}".format(id(eachClass)))
        eachClass = ["time" if x[2] == ':' else x for x in eachClass]
        print("ID after: {}".format(id(eachClass)))
        return schedule

timeConvert2([["abc", "09:09", "10:10"], ["def", "11:11", "12:12"]])

OUTPUT

ID before: 139823659735560
ID after: 139823659759816
[['abc', '09:09', '10:10'], ['def', '11:11', '12:12']]

You can see that the ID of eachClass has changed before and after assignment which essentially means that you have created a new object.

def timeConvert1(schedule):
    for eachClass in schedule:
        print("ID before: {}".format(id(eachClass)))
        eachClass[:] = ["time" if x[2] == ':' else x for x in eachClass]
        print("ID after: {}".format(id(eachClass)))
        return schedule

timeConvert1([["abc", "09:09", "10:10"], ["def", "11:11", "12:12"]])

OUTPUT

ID before: 139823659594440
ID after: 139823659594440
[['abc', 'time', 'time'], ['def', '11:11', '12:12']]

You can see that the ID of eachClass has not changed before and after assignment which essentially means that you are working with the same object.

Sunderam Dubey
  • 1
  • 11
  • 20
  • 40
Parthasarathy Subburaj
  • 4,106
  • 2
  • 10
  • 24