cmarios
cmarios

Reputation: 185

Consecutive if statements: order of execution

Here is my Python code:

my_constant = 5
my_constant_2 = 6
list_of_lists = [[3,2],[4,7]]

my_new_list = []

for i in list_of_lists:
    my_dict = {}
    for j in i:    
        if j > my_constant:
            my_dict["a"] = "Hello"
            my_dict["b"] = "Hello"
            print(my_dict)
            my_new_list.append(my_dict)
        if j > my_constant_2:
            my_dict["a"] = "Hello"
            my_dict["b"] = "Bye"
            print(my_dict)
            my_new_list.append(my_dict)


print(my_new_list)

Here are the results:

{'a': 'Hello', 'b': 'Hello'}
{'a': 'Hello', 'b': 'Bye'}
[{'a': 'Hello', 'b': 'Bye'}, {'a': 'Hello', 'b': 'Bye'}]

The first two lines of the results are according to my expectations, but the third line is not. I would expect this:

[{'a': 'Hello', 'b': 'Hello'}, {'a': 'Hello', 'b': 'Bye'}]

So it looks that when the loop waits for the second "if" before appending to my_new_list, and us such the my_new_list get twice the new my_dict.

I know that the below code solves the issue (ie moving the my_dict inside the "if"):

my_constant = 5
my_constant_2 = 6
list_of_lists = [[3,2],[4,7]]

my_new_list = []

for i in list_of_lists:
    for j in i:        
        if j > my_constant:
            my_dict = {}
            my_dict["a"] = "Hello"
            my_dict["b"] = "Hello"
            print(my_dict)
            my_new_list.append(my_dict)
        if j > my_constant_2:
            my_dict = {}
            my_dict["a"] = "Hello"
            my_dict["b"] = "Bye"
            print(my_dict)
            my_new_list.append(my_dict)


print(my_new_list)

However, in practice my_dict is not empty and a function is used to create it (and it takes some time). Also, there are more "ifs", so i prefer not to use the above code.

Is there a trick to get over it?

Thanks.

Upvotes: 0

Views: 224

Answers (3)

Lead Developer
Lead Developer

Reputation: 1169

my_constant = 5
my_constant_2 = 6
list_of_lists = [[3,2],[4,7]]

my_new_list = []

for i in list_of_lists:
    my_dict = {}
    for j in i:    
        if j > my_constant:
            my_dict["a"] = "Hello"
            my_dict["b"] = "Hello"
            print(my_dict)
            my_new_list.append(my_dict.copy())
        if j > my_constant_2:
            my_dict["a"] = "Hello"
            my_dict["b"] = "Bye"
            print(my_dict)
            my_new_list.append(my_dict.copy())


print(my_new_list)

because in your code, my_new_list[0] and my_new_list[1] is same object. So when you change the my_dict in if j > my_constant_2 block, then will be changed my_new_list[0] too. please use copy or deepcopy when you add the object into my_new_list. copy() function will do the shallow copy. So it's good to use deepcopy for safety.

and I think my_dict initialization position is not proper. please think about that.

Upvotes: 0

Aaditya Ura
Aaditya Ura

Reputation: 12669

You are re-using same object again and again

use deepcopy:

from copy import deepcopy
my_constant = 5

my_constant_2 = 6
list_of_lists = [[3,2],[4,7]]

my_new_list = []

for i in list_of_lists:
    my_dict = {}
    for j in i:
        if j > my_constant:
            my_dict["a"] = "Hello"
            my_dict["b"] = "Hello"
            print(my_dict)
            my_new_list.append(deepcopy(my_dict))
        if j > my_constant_2:
            my_dict["a"] = "Hello"
            my_dict["b"] = "Bye"
            print(my_dict)
            my_new_list.append(my_dict)


print(my_new_list)

output:

{'b': 'Hello', 'a': 'Hello'}
{'b': 'Bye', 'a': 'Hello'}
[{'b': 'Hello', 'a': 'Hello'}, {'b': 'Bye', 'a': 'Hello'}]

Why deepcopy() why not copy ?

Additional explanation :

For understanding of deepcopy vs copy here is example code :

Let's explore your issue by a little example:

so suppose you have a list:

a=['something']

And there is also a second list which contains:

list_1=[a,a,a]

So when you do:

a[0]="something_else"

what do you think what is the output of list_1 ?

Let's check:

a=['something']
list_1=[a,a,a]


a[0]="something_else"
print(list_1)

output:

[['something_else'], ['something_else'], ['something_else']]

Because in python variable doesn't store the value , variable are just referece to the object and object store the value so in list_1 all the variable are pointing to the same object:

checking:

for i in list_1:
    print(id(i))

output:

4329477768
4329477768
4329477768

Try deepcopy:

why deepcopy why not copy ?

suppose you have dict like this:

dict_cell = {'item1': [20,34], 'item2': [25,9]}

you run your code with this dict_cell and you got the output:

[[{'item2': [25, 9], 'item1': 2}, {'item2': [25, 9], 'item1': [20, 34]}], [{'item2': [25, 9], 'item1': [20, 34]}, {'item2': [25, 9], 'item1': [20, 34]}]]

Now let's try to change the original dict values :

dict_cell = {'item1': [20,34], 'item2': [25,9]}
width = 2
height = 2
array = []
for i in range(height):
    row=[]
    for j in range(width):
        row.append(dict_cell.copy())
    array.append(row)
array[0][0]['item1'] =2


for key,value in dict_cell.items():
    value[0]='changed'

print(array,'\n')

output:

[[{'item1': 2, 'item2': ['changed', 9]}, {'item1': ['changed', 34], 'item2': ['changed', 9]}], [{'item1': ['changed', 34], 'item2': ['changed', 9]}, {'item1': ['changed', 34], 'item2': ['changed', 9]}]] 

we modifed the orional dict but it changed the content in array list's dict because that is shallow copy of dict. it copied the dict but didn't copy the nested list.

Solution :

Use deepcopy.

Upvotes: 1

Lev Levitsky
Lev Levitsky

Reputation: 65791

The difference between the two versions of the code is that the first one appends the same dict to a list multiple times. The list will always contain the same dict repeated in this case.

However, nothing stops you from filling my_dict with pre-computed values:

defaults = {'some': 'common', 'values': 'here'}

for i in list_of_lists:
    for j in i:        
        if j > my_constant:
            my_dict = defaults.copy()
            my_dict["a"] = "Hello"
            my_dict["b"] = "Hello"
            print(my_dict)
            my_new_list.append(my_dict)

defaults can be calculated inside the loop if it depends on i or j.

If you are concerned with data duplication, just change the logic of your program and don't store repeating values in my_new_list.

Upvotes: 0

Related Questions