Umair Ayub
Umair Ayub

Reputation: 21261

Python code produces incorrect results without using the .COPY() function

I have this code.

List1 = [{'pg_id': 100, "group_name": "test1", "product_price": 100}, {'pg_id': 200, "group_name": "test2", "product_price": 200}]
List2 = [{'lowest_price': 20}]

FINAL_DICT = {}
for latest_lowest in List1:
    for idx, coupon_related__product in enumerate(List2):
        if coupon_related__product['lowest_price'] < latest_lowest['product_price']:
            FINAL_DICT[ latest_lowest['pg_id'] ] = coupon_related__product
            FINAL_DICT[ latest_lowest['pg_id'] ]['group_name'] = latest_lowest['group_name']
            print("Group name is %s"%(latest_lowest['group_name']))

for PG_ID, LOWEST_PRICED_PRODUCT_THIS_PG in FINAL_DICT.iteritems():
    print(LOWEST_PRICED_PRODUCT_THIS_PG)

My desired output in terminal is

Group name is test1
Group name is test2
{'lowest_price': 20, 'group_name': 'test2'}
{'lowest_price': 20, 'group_name': 'test1'}

But it outputs

Group name is test1
Group name is test2
{'lowest_price': 20, 'group_name': 'test2'}
{'lowest_price': 20, 'group_name': 'test2'}

If I change

FINAL_DICT[ latest_lowest['pg_id'] ] = coupon_related__product

to

FINAL_DICT[ latest_lowest['pg_id'] ] = coupon_related__product.copy()

Then it produces correct output which I want.

My question is, why all of the dictionaries in FINAL_DICT has group_name=test2 when I don't use .copy()

Upvotes: 0

Views: 58

Answers (2)

Jean-Fran&#231;ois Fabre
Jean-Fran&#231;ois Fabre

Reputation: 140186

this line:

FINAL_DICT[ latest_lowest['pg_id'] ] = coupon_related__product

assigns a dictionary to your entry. The next line:

FINAL_DICT[ latest_lowest['pg_id'] ]['group_name'] = latest_lowest['group_name']

changes the group name, but the reference of the dict is the last dictionary you assigned to, so the reference is shared.

that's why you have to make an copy of the dict so references are independent:

FINAL_DICT[ latest_lowest['pg_id'] ] = coupon_related__product.copy()

now modifying FINAL_DICT[ latest_lowest['pg_id'] ]['group_name'] doesn't change the values of others FINAL_DICT[ latest_lowest['pg_id'] ]

Upvotes: 2

Nick Chapman
Nick Chapman

Reputation: 4634

In Python dictionaries are mutable objects and are therefore not copied by default. You can read more about this here: python dictionary passed as an input to a function acts like a global in that function rather than a local

If the whole pass by name vs pass by value vs pass by reference thing is too confusing then all you need to know is that Python does not normally make a copy of a list or dict when assigning values using =. When you want to make a copy of a list or dict you need to explicitly tell Python you would like a copy. So in summary

x = {"hello": "world"}
y = x
y["hello"] = "not world"
print("x is " + str(x))
print("y is " + str(y))

> x is {'hello': 'not world'}
> y is {'hello': 'not world'}

vs

x = {"hello": "world"}
y = x.copy()
y["hello"] = "not world"
print("x is " + str(x))
print("y is " + str(y))

> x is {'hello': 'world'}
> y is {'hello': 'not world'}

Upvotes: 1

Related Questions