Currently, I am using Python and JavaScript simultaneously to automate tests for two projects using Selenium and Postman. I am relatively new to and had a tight timeline to learn it. So, I challenged myself to learn its “vanilla basics” in just seven days. I noticed that the features of its object type are quite similar to Python's dictionary data structure, including the assignment, shallow copy, and deep copy features which I will share in my next articles. JavaScript In this short article, we will use the keyword and operator to understand the Pythonic concepts of assignment, shallow and deep copy. is == ‘Is’ and ‘==’ differ? How do The keyword checks if two variables while checks if two variables have the . is point to the same object == same value or if they are equal to the same value This sounds simple but can get complex in practical scenarios: >>> a = 256 >>> b = 256 >>> a is b True >>> id(a), id(b) # because they point to the same memory location (140501052082576, 140501052082576) >>> a == b True In the above example, variables and point to the same memory location, , in my computer RAM after I assigned and with the same value of 256. In other words, they are the same object with two different variable names. a b 140501052082576 a b So, both the statements and are as expected. a is b a == b True But things get a bit weird if we change the value to 257. >>> a = 257 >>> b = 257 >>> a == b True >>> a is b # The twist is here False The twist is that and now point to two different RAM locations. They are no more the same object! a b The Proof of Concept: >>> id(a), id(b) (140501049663216, 140501049662992) says Python Documentation The current implementation keeps an array of integer objects for all integers between and . When you create an in that range you actually just get back a reference to the existing object. -5 256 int Now we are equipped with two important pieces of information: compares if two variables the is refer to same object. In other words, if both the variables are the same object in a memory location compares if two variables the == have same value Let’s utilize our knowledge to understand different types of copies in Python. Assignment, Shallow and Deep Copy Assignment First, we will declare and populate a with some data and assign it to . Now, and will point to the same object. dict_1 dict_2 dict_1 dict_1 # Example 1 dict_1 = { 'guitar': 'Yamaha', 'strings': 7, 'pedals':{ 'cry_baby': 'Electro-Harmonix', 'distortion': 'Boss', 'delay': 'TC Electronics' }, 'numbers': [1,2,3], } dict_2 = dict_1 print(id(dict_1), id(dict_2)) print(dict_1 is dict_2) """ $ python3 example.py 140283083210048 140283083210048 True """ Any changes in either of the variable, , will bring change on the reference object that will be reflected in both variables. key:value any type or nested So, both their values and reference objects remain the same. # Example 2 dict_2['delay'] = 'Line 6' print(id(dict_1), id(dict_2)) print(dict_1 == dict_2) print(dict_1 is dict_2) """ $ python3 examples.py 140319473249600 140319473249600 True True """ Shallow Copy ’s builtin module lets us do and copies. Let’s start with a shallow copy. Python 3x copy shallow deep A c and then (to the extent possible) inserts into it to the objects found in the original. shallow copy onstructs a new compound object references In other words, a shallow copy creates a new object that holds a reference to the objects found in the parent or original variable. So, they are different objects. # Example 3 import copy dict_1 = { 'guitar': 'Yamaha', 'strings': 7, 'pedals':{ 'cry_baby': 'Electro-Harmonix', 'distortion': 'Boss', 'delay': 'TC Electronics' }, 'numbers': [1,2,3], } dict_2 = copy.copy(dict_1) print(id(dict_1), id(dict_2)) print(id(dict_1['guitar']), id(dict_2['guitar'])) print(id(dict_1['pedals']), id(dict_2['pedals'])) """ $ python hello.py 1448723023744 1448723087936 # Different objects created 1448723023792 1448723023792 # Points to same guitar 1448723023488 1448723023488 # Points to same pedals """ Notice two things: and have two different memory addresses. Here, holds reference to the objects found in dict_1 dict_2 dict_2 dict_1 holds reference to and objects in dict_2 guitar pedal dict_1 If we bring any change in the or non-nested values in that will not affect and vice-versa. statement 1 immutable dict_1 dict_2 If we bring any change in the or nested values in that will affect and vice-versa. statement 2 mutable dict_1 dict_2 Shallow Copy: Changes on nested or mutable data Let’s start with . Let’s change the value of into . statement 2 distortion dict_2 Zoom Take a deep breath and some time to understand what just happened: # Example 4 dict_2['pedals']['distortion'] = "Zoom" print(dict_1 is dict_2) print(dict_2 == dict_1) """ $ python3 example.py False True """ 💡 The change made in the nested dictionary in were brought in two different objects, and situated at in my computer RAM. So, returns . But both the objects have the same value and the statement returns . Explanation dict_2 dict_1 dict_1 1448723023744 and 1448723087936 dict_1 is dict_2 False dict_2 == dict_1 True Why does this happen? Because, when we bring any change in this refers to the same memory location in the RAM. pedals Have a look at the PoC print(f"Before Change id(dict_2['pedals']): {id(dict_2['pedals'])}\ id(dict_1['pedals']): {id(dict_1['pedals'])}") dict_2['pedals']['distortion'] = "Zoom" print(f"After Change id(dict_2['pedals']): {id(dict_2['pedals'])}\ id(dict_1['pedals']): {id(dict_1['pedals'])}") """ $ python3 example.py Before Change id(dict_2['pedals']): 140285477350656 id(dict_1['pedals']): 140285477350656 After Change id(dict_2['pedals']): 140285477350656 id(dict_1['pedals']): 140285477350656 """ Task Modify the in and see what happens to numbers dict_1 dict_2 Shallow Copy: Changes on immutable data Let’s get back to that says “If we bring any change in the or non-nested values in that will not affect and vice-versa.” statement 1 immutable dict_1 dict_2 Bring a change in the key’s value in and see the effect guitar dict_1 dict_1['guitar'] = 'Ibanez' print(dict_1 is dict_2) print(dict_2 == dict_1) """ $ python3 example.py False False """ 💡 The change brought in 's value takes place in two different objects as well. But, 's now points to a different memory location that left 's untouched. Explanation dict_1 guitar dict_1 guitar dict_2 guitar So, neither and are same objects nor they have same values. dict_1 dict_2 Have a look at the PoC print("Before assignment") print(dict_1 is dict_2) print(id(dict_1['guitar']), id(dict_2['guitar']) ) print(dict_1['guitar'] is dict_2['guitar']) dict_1['guitar'] = 'Ibanez' print("After assignment") print(dict_1 is dict_2) print(id(dict_1['guitar']), id(dict_2['guitar']) ) print(dict_1['guitar'] is dict_2['guitar']) """ $ python3 example.py Before assignment False 139863551636464 139863551636464 True After assignment False 139863550623600 139863551636464 False """ Deep Copy If a reader came here reading the whole text along with executing the examples, this segment needs only a few words and examples Python Documentation says A constructs a new compound object and then, recursively, inserts into it of the objects found in the original. deep copy copies In other words, a deep copy creates a new object that recursively creates a separate copy of the objects from the parent / original variable. A deep copy ensures that any changes in the original or assigned variable will not affect each other. import copy dict_1 = { 'guitar': 'Yamaha', 'strings': 7, 'pedals':{ 'cry_baby': 'Electro-Harmonix', 'distortion': 'Boss', 'delay': 'TC Electronics' }, 'numbers': [1,2,3], } dict_2 = copy.deepcopy(dict_1) print(id(dict_2), id(dict_1)) # ...1 print(id(dict_2['strings']), id(dict_1['strings'])) # ...2 print(id(dict_2['pedals']), id(dict_1['pedals'])) # ...3 """ $ python3 Shallow.py 140331302249472 140331302661568 140331303287280 140331303287280 140331300866368 140331302661376 """ 💡 Explanation Notice that a of deepcopy() dict_1 Assigns a separate memory location for dict_2 Any changes in key will point to a new object/memory location just like a shallow copy strings Assigns different memory location for mutable and nested data. This ensures that any changes in or will affect each other. dict_2 dict_1 dict_2 Wrapping it up Almost every available write-up on deep and shallow copy in Python use nested list examples. I found this data type leaves a noob to intermediate programmer a bit confused, at least they need some extra effort to get the concept. While learning copying JS objects, I found dictionary data structure would help them to visualize the Pythonic concept of deep and shallow copy concepts a bit more clear.