- Python »
- 3.13.0 Documentation »
- The Python Standard Library »
- Data Types »
- copy — Shallow and deep copy operations
- Theme Auto Light Dark |
copy — Shallow and deep copy operations ¶
Source code: Lib/copy.py
Assignment statements in Python do not copy objects, they create bindings between a target and an object. For collections that are mutable or contain mutable items, a copy is sometimes needed so one can change one copy without changing the other. This module provides generic shallow and deep copy operations (explained below).
Interface summary:
Return a shallow copy of obj .
Return a deep copy of obj .
Creates a new object of the same type as obj , replacing fields with values from changes .
Added in version 3.13.
Raised for module specific errors.
The difference between shallow and deep copying is only relevant for compound objects (objects that contain other objects, like lists or class instances):
A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.
Two problems often exist with deep copy operations that don’t exist with shallow copy operations:
Recursive objects (compound objects that, directly or indirectly, contain a reference to themselves) may cause a recursive loop.
Because deep copy copies everything it may copy too much, such as data which is intended to be shared between copies.
The deepcopy() function avoids these problems by:
keeping a memo dictionary of objects already copied during the current copying pass; and
letting user-defined classes override the copying operation or the set of components copied.
This module does not copy types like module, method, stack trace, stack frame, file, socket, window, or any similar types. It does “copy” functions and classes (shallow and deeply), by returning the original object unchanged; this is compatible with the way these are treated by the pickle module.
Shallow copies of dictionaries can be made using dict.copy() , and of lists by assigning a slice of the entire list, for example, copied_list = original_list[:] .
Classes can use the same interfaces to control copying that they use to control pickling. See the description of module pickle for information on these methods. In fact, the copy module uses the registered pickle functions from the copyreg module.
In order for a class to define its own copy implementation, it can define special methods __copy__() and __deepcopy__() .
Called to implement the shallow copy operation; no additional arguments are passed.
Called to implement the deep copy operation; it is passed one argument, the memo dictionary. If the __deepcopy__ implementation needs to make a deep copy of a component, it should call the deepcopy() function with the component as first argument and the memo dictionary as second argument. The memo dictionary should be treated as an opaque object.
Function copy.replace() is more limited than copy() and deepcopy() , and only supports named tuples created by namedtuple() , dataclasses , and other classes which define method __replace__() .
This method should create a new object of the same type, replacing fields with values from changes .
Discussion of the special methods used to support object state retrieval and restoration.
Table of Contents
- __deepcopy__()
- __replace__()
Previous topic
types — Dynamic type creation and names for built-in types
pprint — Data pretty printer
- Report a Bug
- Show Source
- Module 2: The Essentials of Python »
- Variables & Assignment
- View page source
Variables & Assignment
There are reading-comprehension exercises included throughout the text. These are meant to help you put your reading to practice. Solutions for the exercises are included at the bottom of this page.
Variables permit us to write code that is flexible and amendable to repurpose. Suppose we want to write code that logs a student’s grade on an exam. The logic behind this process should not depend on whether we are logging Brian’s score of 92% versus Ashley’s score of 94%. As such, we can utilize variables, say name and grade , to serve as placeholders for this information. In this subsection, we will demonstrate how to define variables in Python.
In Python, the = symbol represents the “assignment” operator. The variable goes to the left of = , and the object that is being assigned to the variable goes to the right:
Attempting to reverse the assignment order (e.g. 92 = name ) will result in a syntax error. When a variable is assigned an object (like a number or a string), it is common to say that the variable is a reference to that object. For example, the variable name references the string "Brian" . This means that, once a variable is assigned an object, it can be used elsewhere in your code as a reference to (or placeholder for) that object:
Valid Names for Variables
A variable name may consist of alphanumeric characters ( a-z , A-Z , 0-9 ) and the underscore symbol ( _ ); a valid name cannot begin with a numerical value.
var : valid
_var2 : valid
ApplePie_Yum_Yum : valid
2cool : invalid (begins with a numerical character)
I.am.the.best : invalid (contains . )
They also cannot conflict with character sequences that are reserved by the Python language. As such, the following cannot be used as variable names:
for , while , break , pass , continue
in , is , not
if , else , elif
def , class , return , yield , raises
import , from , as , with
try , except , finally
There are other unicode characters that are permitted as valid characters in a Python variable name, but it is not worthwhile to delve into those details here.
Mutable and Immutable Objects
The mutability of an object refers to its ability to have its state changed. A mutable object can have its state changed, whereas an immutable object cannot. For instance, a list is an example of a mutable object. Once formed, we are able to update the contents of a list - replacing, adding to, and removing its elements.
To spell out what is transpiring here, we:
Create (initialize) a list with the state [1, 2, 3] .
Assign this list to the variable x ; x is now a reference to that list.
Using our referencing variable, x , update element-0 of the list to store the integer -4 .
This does not create a new list object, rather it mutates our original list. This is why printing x in the console displays [-4, 2, 3] and not [1, 2, 3] .
A tuple is an example of an immutable object. Once formed, there is no mechanism by which one can change of the state of a tuple; and any code that appears to be updating a tuple is in fact creating an entirely new tuple.
Mutable & Immutable Types of Objects
The following are some common immutable and mutable objects in Python. These will be important to have in mind as we start to work with dictionaries and sets.
Some immutable objects
numbers (integers, floating-point numbers, complex numbers)
“frozen”-sets
Some mutable objects
dictionaries
NumPy arrays
Referencing a Mutable Object with Multiple Variables
It is possible to assign variables to other, existing variables. Doing so will cause the variables to reference the same object:
What this entails is that these common variables will reference the same instance of the list. Meaning that if the list changes, all of the variables referencing that list will reflect this change:
We can see that list2 is still assigned to reference the same, updated list as list1 :
In general, assigning a variable b to a variable a will cause the variables to reference the same object in the system’s memory, and assigning c to a or b will simply have a third variable reference this same object. Then any change (a.k.a mutation ) of the object will be reflected in all of the variables that reference it ( a , b , and c ).
Of course, assigning two variables to identical but distinct lists means that a change to one list will not affect the other:
Reading Comprehension: Does slicing a list produce a reference to that list?
Suppose x is assigned a list, and that y is assigned a “slice” of x . Do x and y reference the same list? That is, if you update part of the subsequence common to x and y , does that change show up in both of them? Write some simple code to investigate this.
Reading Comprehension: Understanding References
Based on our discussion of mutable and immutable objects, predict what the value of y will be in the following circumstance:
Reading Comprehension Exercise Solutions:
Does slicing a list produce a reference to that list?: Solution
Based on the following behavior, we can conclude that slicing a list does not produce a reference to the original list. Rather, slicing a list produces a copy of the appropriate subsequence of the list:
Understanding References: Solutions
Integers are immutable, thus x must reference an entirely new object ( 9 ), and y still references 3 .
previous episode
Python for absolute beginners, next episode, variables and assignment.
Overview Teaching: 15 min Exercises: 15 min Questions How can I store data in programs? Objectives Write scripts that assign values to variables and perform calculations with those values. Correctly trace value changes in scripts that use assignment.
Use variables to store values
Variables are one of the fundamental building blocks of Python. A variable is like a tiny container where you store values and data, such as filenames, words, numbers, collections of words and numbers, and more.
The variable name will point to a value that you “assign” it. You might think about variable assignment like putting a value “into” the variable, as if the variable is a little box 🎁
(In fact, a variable is not a container as such but more like an adress label that points to a container with a given value. This difference will become relevant once we start talking about lists and mutable data types.)
You assign variables with an equals sign ( = ). In Python, a single equals sign = is the “assignment operator.” (A double equals sign == is the “real” equals sign.)
- Variables are names for values.
- In Python the = symbol assigns the value on the right to the name on the left.
- The variable is created when a value is assigned to it.
- Here, Python assigns an age to a variable age and a name in quotation marks to a variable first_name :
Variable names
Variable names can be as long or as short as you want, but there are certain rules you must follow.
- Cannot start with a digit.
- Cannot contain spaces, quotation marks, or other punctuation.
- May contain an underscore (typically used to separate words in long variable names).
- Having an underscore at the beginning of a variable name like _alistairs_real_age has a special meaning. So we won’t do that until we understand the convention.
- The standard naming convention for variable names in Python is the so-called “snake case”, where each word is separated by an underscore. For example my_first_variable . You can read more about naming conventions in Python here .
Use meaningful variable names
Python doesn’t care what you call variables as long as they obey the rules (alphanumeric characters and the underscore). As you start to code, you will almost certainly be tempted to use extremely short variables names like f . Your fingers will get tired. Your coffee will wear off. You will see other people using variables like f . You’ll promise yourself that you’ll definitely remember what f means. But you probably won’t.
So, resist the temptation of bad variable names! Clear and precisely-named variables will:
- Make your code more readable (both to yourself and others).
- Reinforce your understanding of Python and what’s happening in the code.
- Clarify and strengthen your thinking.
Use meaningful variable names to help other people understand what the program does. The most important “other person” is your future self!
Python is case-sensitive
Python thinks that upper- and lower-case letters are different, so Name and name are different variables. There are conventions for using upper-case letters at the start of variable names so we will use lower-case letters for now.
Off-Limits Names
The only variable names that are off-limits are names that are reserved by, or built into, the Python programming language itself — such as print , True , and list . Some of these you can overwrite into variable names (not ideal!), but Jupyter Lab (and many other environments and editors) will catch this by colour coding your variable. If your would-be variable is colour-coded green, rethink your name choice. This is not something to worry too much about. You can get the object back by resetting your kernel.
Use print() to display values
We can check to see what’s “inside” variables by running a cell with the variable’s name. This is one of the handiest features of a Jupyter notebook. Outside the Jupyter environment, you would need to use the print() function to display the variable.
You can run the print() function inside the Jupyter environment, too. This is sometimes useful because Jupyter will only display the last variable in a cell, while print() can display multiple variables. Additionally, Jupyter will display text with \n characters (which means “new line”), while print() will display the text appropriately formatted with new lines.
- Python has a built-in function called print() that prints things as text.
- Provide values to the function (i.e., the things to print) in parentheses.
- To add a string to the printout, wrap the string in single or double quotations.
- The values passed to the function are called ‘arguments’ and are separated by commas.
- When using the print() function, we can also separate with a ‘+’ sign. However, when using ‘+’ we have to add spaces in between manually.
- print() automatically puts a single space between items to separate them.
- And wraps around to a new line at the end.
Variables must be created before they are used
If a variable doesn’t exist yet, or if the name has been misspelled, Python reports an error (unlike some languages, which “guess” a default value).
The last line of an error message is usually the most informative. This message lets us know that there is no variable called eye_color in the script.
Variables Persist Between Cells Variables defined in one cell exist in all other cells once executed, so the relative location of cells in the notebook do not matter (i.e., cells lower down can still affect those above). Notice the number in the square brackets [ ] to the left of the cell. These numbers indicate the order, in which the cells have been executed. Cells with lower numbers will affect cells with higher numbers as Python runs the cells chronologically. As a best practice, we recommend you keep your notebook in chronological order so that it is easier for the human eye to read and make sense of, as well as to avoid any errors if you close and reopen your project, and then rerun what you have done. Remember: Notebook cells are just a way to organize a program! As far as Python is concerned, all of the source code is one long set of instructions.
Variables can be used in calculations
- We can use variables in calculations just as if they were values. Remember, we assigned 42 to age a few lines ago.
This code works in the following way. We are reassigning the value of the variable age by taking its previous value (42) and adding 3, thus getting our new value of 45.
Use an index to get a single character from a string
- The characters (individual letters, numbers, and so on) in a string are ordered. For example, the string ‘AB’ is not the same as ‘BA’. Because of this ordering, we can treat the string as a list of characters.
- Each position in the string (first, second, etc.) is given a number. This number is called an index or sometimes a subscript.
- Indices are numbered from 0 rather than 1.
- Use the position’s index in square brackets to get the character at that position.
Use a slice to get a substring
A part of a string is called a substring. A substring can be as short as a single character. A slice is a part of a string (or, more generally, any list-like thing). We take a slice by using [start:stop] , where start is replaced with the index of the first element we want and stop is replaced with the index of the element just after the last element we want. Mathematically, you might say that a slice selects [start:stop] . The difference between stop and start is the slice’s length. Taking a slice does not change the contents of the original string. Instead, the slice is a copy of part of the original string.
Use the built-in function len() to find the length of a string
The built-in function len() is used to find the length of a string (and later, of other data types, too).
Note that the result is 6 and not 7. This is because it is the length of the value of the variable (i.e. 'helium' ) that is being counted and not the name of the variable (i.e. element )
Also note that nested functions are evaluated from the inside out, just like in mathematics. Thus, Python first reads the len() function, then the print() function.
Choosing a Name Which is a better variable name, m , min , or minutes ? Why? Hint: think about which code you would rather inherit from someone who is leaving the library: ts = m * 60 + s tot_sec = min * 60 + sec total_seconds = minutes * 60 + seconds Solution minutes is better because min might mean something like “minimum” (and actually does in Python, but we haven’t seen that yet).
Swapping Values Draw a table showing the values of the variables in this program after each statement is executed. In simple terms, what do the last three lines of this program do? x = 1.0 y = 3.0 swap = x x = y y = swap Solution swap = x # x->1.0 y->3.0 swap->1.0 x = y # x->3.0 y->3.0 swap->1.0 y = swap # x->3.0 y->1.0 swap->1.0 These three lines exchange the values in x and y using the swap variable for temporary storage. This is a fairly common programming idiom.
Predicting Values What is the final value of position in the program below? (Try to predict the value without running the program, then check your prediction.) initial = "left" position = initial initial = "right" Solution initial = "left" # Initial is assigned the string "left" position = initial # Position is assigned the variable initial, currently "left" initial = "right" # Initial is assigned the string "right" print(position) left The last assignment to position was “left”
Can you slice integers? If you assign a = 123 , what happens if you try to get the second digit of a ? Solution Numbers are not stored in the written representation, so they can’t be treated like strings. a = 123 print(a[1]) TypeError: 'int' object is not subscriptable
Slicing What does the following program print? library_name = 'social sciences' print('library_name[1:3] is:', library_name[1:3]) If thing is a variable name, low is a low number, and high is a high number: What does thing[low:high] do? What does thing[low:] (without a value after the colon) do? What does thing[:high] (without a value before the colon) do? What does thing[:] (just a colon) do? What does thing[number:negative-number] do? Solution library_name[1:3] is: oc It will slice the string, starting at the low index and ending an element before the high index It will slice the string, starting at the low index and stopping at the end of the string It will slice the string, starting at the beginning on the string, and ending an element before the high index It will print the entire string It will slice the string, starting the number index, and ending a distance of the absolute value of negative-number elements from the end of the string
Key Points Use variables to store values. Use meaningful variable names. Python is case-sensitive. Use print() to display values. Variables must be created before they are used. Variables persist between cells. Variables can be used in calculations. Use an index to get a single character from a string. Use a slice to get a substring. Use the built-in function len to find the length of a string.
Pass-by-value, reference, and assignment | Pydon't 🐍
When you call a function in Python and give it some arguments... Are they passed by value? No! By reference? No! They're passed by assignment.
(If you are new here and have no idea what a Pydon't is, you may want to read the Pydon't Manifesto .)
Introduction
Many traditional programming languages employ either one of two models when passing arguments to functions:
- some languages use the pass-by-value model; and
- most of the others use the pass-by-reference model.
Having said that, it is important to know the model that Python uses, because that influences the way your code behaves.
In this Pydon't, you will:
- see that Python doesn't use the pass-by-value nor the pass-by-reference models;
- understand that Python uses a pass-by-assignment model;
- learn about the built-in function id ;
- create a better understanding for the Python object model;
- realise that every object has 3 very important properties that define it;
- understand the difference between mutable and immutable objects;
- learn the difference between shallow and deep copies; and
- learn how to use the module copy to do both types of object copies.
You can now get your free copy of the ebook “Pydon'ts – Write elegant Python code” on Gumroad .
Is Python pass-by-value?
In the pass-by-value model, when you call a function with a set of arguments, the data is copied into the function. This means that you can modify the arguments however you please and that you won't be able to alter the state of the program outside the function. This is not what Python does, Python does not use the pass-by-value model.
Looking at the snippet of code that follows, it might look like Python uses pass-by-value:
This looks like the pass-by-value model because we gave it a 3, changed it to a 4, and the change wasn't reflected on the outside ( a is still 3).
But, in fact, Python is not copying the data into the function.
To prove this, I'll show you a different function:
As we can see, the list l , that was defined outside of the function, changed after calling the function clearly_not_pass_by_value . Hence, Python does not use a pass-by-value model.
Is Python pass-by-reference?
In a true pass-by-reference model, the called function gets access to the variables of the callee! Sometimes, it can look like that's what Python does, but Python does not use the pass-by-reference model.
I'll do my best to explain why that's not what Python does:
If Python used a pass-by-reference model, the function would've managed to completely change the value of l outside the function, but that's not what happened, as we can see.
Let me show you an actual pass-by-reference situation.
Here's some Pascal code:
Look at the last lines of that code:
- we assign 2 to x with x := 2 ;
- we print x ;
- we call foo with x as argument; and
- we print x again.
What's the output of this program?
I imagine that most of you won't have a Pascal interpreter lying around, so you can just go to tio.run and run this code online
If you run this, you'll see that the output is
which can be rather surprising, if the majority of your programming experience is in Python!
The procedure foo effectively received the variable x and changed the value that it contained. After foo was done, the variable x (that lives outside foo ) had a different value. You can't do anything like this in Python.
Python object model
To really understand the way Python behaves when calling functions, it's best if we first understand what Python objects are, and how to characterise them.
The three characteristics of objects
In Python, everything is an object, and each object is characterised by three things:
- its identity (an integer that uniquely identifies the object, much like social security numbers identify people);
- a type (that identifies the operations you can do with your object); and
- the object's content.
Here is an object and its three characteristics:
As we can see above, id is the built-in function you use to query the identity of an object, and type is the built-in function you use to query the type of an object.
(Im)mutability
The (im)mutability of an object depends on its type. In other words, (im)mutability is a characteristic of types, not of specific objects!
But what exactly does it mean for an object to be mutable? Or for an object to be immutable?
Recall that an object is characterised by its identity, its type, and its contents. A type is mutable if you can change the contents of its objects without changing its identity and its type.
Lists are a great example of a mutable data type. Why? Because lists are containers : you can put things inside lists and you can remove stuff from inside those same lists.
Below, you can see how the contents of the list obj change as we make method calls, but the identity of the list remains the same:
However, when dealing with immutable objects, it's a completely different story. If we check an English dictionary, this is what we get for the definition of “immutable”:
adjective: immutable – unchanging over time or unable to be changed.
Immutable objects' contents never change. Take a string as an example:
Strings are a good example for this discussion because, sometimes, they can look mutable. But they are not!
A very good indicator that an object is immutable is when all its methods return something. This is unlike list's .append method, for example! If you use .append on a list, you get no return value. On the other hand, whatever method you use on a string, the result is returned to you:
Notice how obj wasn't updated automatically to "HELLO, WORLD!" . Instead, the new string was created and returned to you.
Another great hint at the fact that strings are immutable is that you cannot assign to its indices:
This shows that, when a string is created, it remains the same. It can be used to build other strings, but the string itself always. stays. unchanged.
As a reference, int , float , bool , str , tuple , and complex are the most common types of immutable objects; list , set , and dict are the most common types of mutable objects.
Variable names as labels
Another important thing to understand is that a variable name has very little to do with the object itself.
In fact, the name obj was just a label that I decided to attach to the object that has identity 2698212637504, has the list type, and contents 1, 2, 3.
Just like I attached the label obj to that object, I can attach many more names to it:
Again, these names are just labels. Labels that I decided to stick to the same object. How can we know it's the same object? Well, all their “social security numbers” (the ids) match, so they must be the same object:
Therefore, we conclude that foo , bar , baz , and obj , are variable names that all refer to the same object.
The operator is
This is exactly what the operator is does: it checks if the two objects are the same .
For two objects to be the same, they must have the same identity:
It is not enough to have the same type and contents! We can create a new list with contents [1, 2, 3] and that will not be the same object as obj :
Think of it in terms of perfect twins. When two siblings are perfect twins, they look identical. However, they are different people!
Just as a side note, but an important one, you should be aware of the operator is not .
Generally speaking, when you want to negate a condition, you put a not in front of it:
So, if you wanted to check if two variables point to different objects, you could be tempted to write
However, Python has the operator is not , which is much more similar to a proper English sentence, which I think is really cool!
Therefore, the example above should actually be written
Python does a similar thing for the in operator, providing a not in operator as well... How cool is that?!
Assignment as nicknaming
If we keep pushing this metaphor forward, assigning variables is just like giving a new nickname to someone.
My friends from middle school call me “Rojer”. My friends from college call me “Girão”. People I am not close to call me by my first name – “Rodrigo”. However, regardless of what they call me, I am still me , right?
If one day I decide to change my haircut, everyone will see the new haircut, regardless of what they call me!
In a similar fashion, if I modify the contents of an object, I can use whatever nickname I prefer to see that those changes happened. For example, we can change the middle element of the list we have been playing around with:
We used the nickname foo to modify the middle element, but that change is visible from all other nicknames as well.
Because they all pointed at the same list object.
Python is pass-by-assignment
Having laid out all of this, we are now ready to understand how Python passes arguments to functions.
When we call a function, each of the parameters of the function is assigned to the object they were passed in. In essence, each parameter now becomes a new nickname to the objects that were given in.
Immutable arguments
If we pass in immutable arguments, then we have no way of modifying the arguments themselves. After all, that's what immutable means: “doesn't change”.
That is why it can look like Python uses the pass-by-value model. Because the only way in which we can have the parameter hold something else is by assigning it to a completely different thing. When we do that, we are reusing the same nickname for a different object:
In the example above, when we call foo with the argument 5 , it's as if we were doing bar = 5 at the beginning of the function.
Immediately after that, we have bar = 3 . This means “take the nickname "bar" and point it at the integer 3 ”. Python doesn't care that bar , as a nickname (as a variable name) had already been used. It is now pointing at that 3 !
Mutable arguments
On the other hand, mutable arguments can be changed. We can modify their internal contents. A prime example of a mutable object is a list: its elements can change (and so can its length).
That is why it can look like Python uses a pass-by-reference model. However, when we change the contents of an object, we didn't change the identity of the object itself. Similarly, when you change your haircut or your clothes, your social security number does not change:
Do you understand what I'm trying to say? If not, drop a comment below and I'll try to help.
Beware when calling functions
This goes to show you should be careful when defining your functions. If your function expects mutable arguments, you should do one of the two:
- do not mutate the argument in any way whatsoever; or
- document explicitly that the argument may be mutated.
Personally, I prefer to go with the first approach: to not mutate the argument; but there are times and places for the second approach.
Sometimes, you do need to take the argument as the basis for some kind of transformation, which would mean you would want to mutate the argument. In those cases, you might think about doing a copy of the argument (discussed in the next section), but making that copy can be resource intensive. In those cases, mutating the argument might be the only sensible choice.
Making copies
Shallow vs deep copies.
“Copying an object” means creating a second object that has a different identity (therefore, is a different object) but that has the same contents. Generally speaking, we copy one object so that we can work with it and mutate it, while also preserving the first object.
When copying objects, there are a couple of nuances that should be discussed.
Copying immutable objects
The first thing that needs to be said is that, for immutable objects, it does not make sense to talk about copies.
“Copies” only make sense for mutable objects. If your object is immutable, and if you want to preserve a reference to it, you can just do a second assignment and work on it:
Or, sometimes, you can just call methods and other functions directly on the original, because the original is not going anywhere:
So, we only need to worry about mutable objects.
Shallow copy
Many mutable objects can contain, themselves, mutable objects. Because of that, two types of copies exist:
- shallow copies; and
- deep copies.
The difference lies in what happens to the mutable objects inside the mutable objects.
Lists and dictionaries have a method .copy that returns a shallow copy of the corresponding object.
Let's look at an example with a list:
First, we create a list inside a list, and we copy the outer list. Now, because it is a copy , the copied list isn't the same object as the original outer list:
But if they are not the same object, then we can modify the contents of one of the lists, and the other won't reflect the change:
That's what we saw: we changed the first element of the copy_list , and the outer_list remained unchanged.
Now, we try to modify the contents of sublist , and that's when the fun begins!
When we modify the contents of sublist , both the outer_list and the copy_list reflect those changes...
But wasn't the copy supposed to give me a second list that I could change without affecting the first one? Yes! And that is what happened!
In fact, modifying the contents of sublist doesn't really modify the contents of neither copy_list nor outer_list : after all, the third element of both was pointing at a list object, and it still is! It's the (inner) contents of the object to which we are pointing that changed.
Sometimes, we don't want this to happen: sometimes, we don't want mutable objects to share inner mutable objects.
Common shallow copy techniques
When working with lists, it is common to use slicing to produce a shallow copy of a list:
Using the built-in function for the respective type, on the object itself, also builds shallow copies. This works for lists and dictionaries, and is likely to work for other mutable types.
Here is an example with a list inside a list:
And here is an example with a list inside a dictionary:
When you want to copy an object “thoroughly”, and you don't want the copy to share references to inner objects, you need to do a “deep copy” of your object. You can think of a deep copy as a recursive algorithm.
You copy the elements of the first level and, whenever you find a mutable element on the first level, you recurse down and copy the contents of those elements.
To show this idea, here is a simple recursive implementation of a deep copy for lists that contain other lists:
We can use this function to copy the previous outer_list and see what happens:
As you can see here, modifying the contents of sublist only affected outer_list indirectly; it didn't affect copy_list .
Sadly, the list_deepcopy method I implemented isn't very robust, nor versatile, but the Python Standard Library has got us covered!
The module copy and the method deepcopy
The module copy is exactly what we need. The module provides two useful functions:
- copy.copy for shallow copies; and
- copy.deepcopy for deep copies.
And that's it! And, what is more, the method copy.deepcopy is smart enough to handle issues that might arise with circular definitions, for example! That is, when an object contains another that contains the first one: a naïve recursive implementation of a deep copy algorithm would enter an infinite loop!
If you write your own custom objects and you want to specify how shallow and deep copies of those should be made, you only need to implement __copy__ and __deepcopy__ , respectively!
It's a great module, in my opinion.
Examples in code
Now that we have gone deep into the theory – pun intended –, it is time to show you some actual code that plays with these concepts.
Mutable default arguments
Let's start with a Twitter favourite:
Python 🐍 is an incredible language but sometimes appears to have quirks 🤪 For example, one thing that often confuses beginners is why you shouldn't use lists as default values 👇 Here is a thread 👇🧵 that will help you understand this 💯 pic.twitter.com/HVhPjS2PSH — Rodrigo 🐍📝 (@mathsppblog) October 5, 2021
Apparently, it's a bad idea to use mutable objects as default arguments. Here is a snippet showing you why:
The function above appends an element to a list and, if no list is given, appends it to an empty list by default.
Great, let's put this function to good use:
We use it once with 1 , and we get a list with the 1 inside. Then, we use it to append a 1 to another list we had. And finally, we use it to append a 3 to an empty list... Except that's not what happens!
As it turns out, when we define a function, the default arguments are created and stored in a special place:
What this means is that the default argument is always the same object. Therefore, because it is a mutable object, its contents can change over time. That is why, in the code above, __defaults__ shows a list with two items already.
If we redefine the function, then its __defaults__ shows an empty list:
This is why, in general, mutable objects shouldn't be used as default arguments.
The standard practice, in these cases, is to use None and then use Boolean short-circuiting to assign the default value:
With this implementation, the function now works as expected:
is not None
Searching through the Python Standard Library shows that the is not operator is used a bit over 5,000 times. That's a lot.
And, by far and large, that operator is almost always followed by None . In fact, is not None appears 3169 times in the standard library!
x is not None does exactly what it's written: it checks if x is None or not.
Here is a simple example usage of that, from the argparse module to create command line interfaces:
Even without a great deal of context, we can see what is happening: when displaying command help for a given section, we may want to indent it (or not) to show hierarchical dependencies.
If a section's parent is None , then that section has no parent, and there is no need to indent. In other words, if a section's parent is not None , then we want to indent it. Notice how my English matches the code exactly!
Deep copy of the system environment
The method copy.deepcopy is used a couple of times in the standard library, and here I'd like to show an example usage where a dictionary is copied.
The module os provides the attribute environ , similar to a dictionary, that contains the environment variables that are defined.
Here are a couple of examples from my (Windows) machine:
The module http.server provides some classes for basic HTTP servers.
One of those classes, CGIHTTPRequestHandler , implements a HTTP server that can also run CGI scripts and, in its run_cgi method, it needs to set a bunch of environment variables.
These environment variables are set to give the necessary context for the CGI script that is going to be ran. However, we don't want to actually modify the current environment!
So, what we do is create a deep copy of the environment, and then we modify it to our heart's content! After we are done, we tell Python to execute the CGI script, and we provide the altered environment as an argument.
The exact way in which this is done may not be trivial to understand. I, for one, don't think I could explain it to you. But that doesn't mean we can't infer parts of it:
Here is the code:
As we can see, we copied the environment and defined some variables. Finally, we created a new subprocess that gets the modified environment.
Here's the main takeaway of this Pydon't, for you, on a silver platter:
“ Python uses a pass-by-assignment model, and understanding it requires you to realise all objects are characterised by an identity number, their type, and their contents. ”
This Pydon't showed you that:
- Python doesn't use the pass-by-value model, nor the pass-by-reference one;
- Python uses a pass-by-assignment model (using “nicknames”);
- its identity;
- its type; and
- its contents.
- the id function is used to query an object's identifier;
- the type function is used to query an object's type;
- the type of an object determines whether it is mutable or immutable;
- shallow copies copy the reference of nested mutable objects;
- deep copies perform copies that allow one object, and its inner elements, to be changed without ever affecting the copy;
- copy.copy and copy.deepcopy can be used to perform shallow/deep copies; and
- you can implement __copy__ and __deepcopy__ if you want your own objects to be copiable.
If you prefer video content, you can check this YouTube video , which was inspired by this article.
If you liked this Pydon't be sure to leave a reaction below and share this with your friends and fellow Pythonistas. Also, subscribe to the newsletter so you don't miss a single Pydon't!
Become a better Python 🐍 developer 🚀
+35 chapters. +400 pages. Hundreds of examples. Over 30,000 readers!
My book “Pydon'ts” teaches you how to write elegant, expressive, and Pythonic code, to help you become a better developer. >>> Download it here 🐍🚀 .
- Python 3 Docs, Programming FAQ, “How do I write a function with output parameters (call by reference)?”, https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference [last accessed 04-10-2021];
- Python 3 Docs, The Python Standard Library, copy , https://docs.python.org/3/library/copy.html [last accessed 05-10-2021];
- effbot.org, “Call by Object” (via “arquivo.pt”), https://arquivo.pt/wayback/20160516131553/http://effbot.org/zone/call-by-object.htm [last accessed 04-10-2021];
- effbot.org, “Python Objects” (via “arquivo.pt”), https://arquivo.pt/wayback/20191115002033/http://effbot.org/zone/python-objects.htm [last accessed 04-10-2021];
- Robert Heaton, “Is Python pass-by-reference or pass-by-value”, https://robertheaton.com/2014/02/09/pythons-pass-by-object-reference-as-explained-by-philip-k-dick/ [last accessed 04-10-2021];
- StackOverflow question and answers, “How do I pass a variable by reference?”, https://stackoverflow.com/q/986006/2828287 [last accessed 04-10-2021];
- StackOverflow question and answers, “Passing values in Python [duplicate]”, https://stackoverflow.com/q/534375/2828287 [last accessed 04-10-2021];
- Twitter thread by @mathsppblog , https://twitter.com/mathsppblog/status/1445148566721335298 [last accessed 20-10-2021];
Previous Post Next Post
Random Article
Stay in the loop, popular tags.
- 3 November 2024
- 2 October 2024
- 2 September 2024
- 3 August 2024
- 8 July 2024
- 1 June 2024
- 6 April 2024
- 5 March 2024
- 2 February 2024
- 8 January 2024
- 1 December 2023
Thank you MathGurl for having inspired me so much.
Assignment vs. Mutation in Python
Sign in to change your settings
Sign in to your Python Morsels account to save your screencast settings.
Don't have an account yet? Sign up here .
Let's talk about the two different types of change in Python.
Remember: variables are pointers
When talking about Python code, if I say " we changed X ", there are two different things that I might mean.
Let's say we have two variables that point to the same value:
Remember that variables in Python are pointers . That means that two variables can point to the same object . That's actually what we've done here.
Let's change the object that the variable b points to.
Mutating a list
If we append a number to the list that b points to, we'll see that the length of b is now five:
What's the length of a now?
We appended an item to b , and its length grew. Has a grown also?
Does it have five items in it? Or does it still have four items in it?
Well, a also has five items:
This happened because when we assigned b to a , we pointed two variables to the same object. So changing that object will change the value that both variables point to .
Tuples, numbers, and strings cannot be changed after they've been created. But lists, dictionaries, and sets can be.
Objects that can be changed are called mutable . The act of changing a mutable object is called a mutation .
So when we appended to the list that b points to, we changed that list. But we also saw that change reflected in a , because the variables a and b both happen to point to the same list :
Currently, a and b both point to the same object:
Let's say we change the value of b with an assignment statement :
There are now only three items in b :
But how many items are there in a ?
Are there five items as there were before? Or are there are only three items now? What's your guess?
The list a still has five items in it, so a is unchanged:
The variables a and b originally pointed to the same list:
But then we assigned the variable b to a new object :
That didn't affect a at all. Our a variable still points to the original list:
Assignments versus mutations
Python has two distinct types of change.
Assignment changes a variable. That is, it changes which object a variable points to.
Mutation changes an object, which any number of variables may be pointing to.
So we can change variables through an assignment, and we can change the objects that those variables point to through a mutation.
Changing variables and changing objects
When I say "we changed x ", I could mean two very different things.
I might mean that we mutated the object that x currently points to . Or I might mean that we assigned x to a new object (leaving whatever object it might have pointed to before unchanged ).
Mutations change objects , while assignments change variables (that is, they change which object of variable points to).
A Python tip every week
Need to fill-in gaps in your Python skills?
Sign up for my Python newsletter where I share one of my favorite Python tips every week .
Series: Assignment and Mutation
Python's variables aren't buckets that contain things; they're pointers that reference objects.
The way Python's variables work can often confuse folks new to Python, both new programmers and folks moving from other languages like C++ or Java.
To track your progress on this Python Morsels topic trail, sign in or sign up .
Need to fill-in gaps in your Python skills ? I send weekly emails designed to do just that.
Equality checks whether two objects represent the same value . Identity checks whether two variables point to the same object .
Sign in to record your progress
Sign in to your Python Morsels account to track your progress.
Python Tutorial
File handling, python modules, python numpy, python pandas, python matplotlib, python scipy, machine learning, python mysql, python mongodb, python reference, module reference, python how to, python examples, python assignment operators.
Assignment operators are used to assign values to variables:
Related Pages
COLOR PICKER
Contact Sales
If you want to use W3Schools services as an educational institution, team or enterprise, send us an e-mail: [email protected]
Report Error
If you want to report an error, or if you want to make a suggestion, send us an e-mail: [email protected]
Top Tutorials
Top references, top examples, get certified.
COMMENTS
When you assign dict_a = dict_b, you are really copying a memory address (or pointer, if you will) from dict_b to dict_a. There is still one instance of that dictionary. To get the desired behavior, use either the dict.copy method, or use copy.deepcopy if your dict may have nested dicts or other nested objects. >>> a = {1:2}
The right-hand operand in an assignment statement can be any Python object, such as a number, list, string, dictionary, or even a user-defined object. It can also be an expression. In the end, expressions always evaluate to concrete objects, which is their return value. Here are a few examples of assignments in Python:
Multiple- target assignment: x = y = 75. print(x, y) In this form, Python assigns a reference to the same object (the object which is rightmost) to all the target on the left. OUTPUT. 75 75. 7. Augmented assignment : The augmented assignment is a shorthand assignment that combines an expression and an assignment.
Assignment statements in Python do not copy objects, they create bindings between a target and an object. For collections that are mutable or contain mutable items, a copy is sometimes needed so one can change one copy without changing the other. This module provides generic shallow and deep copy operations (explained below). Interface summary:
Mutable & Immutable Types of Objects The following are some common immutable and mutable objects in Python. These will be important to have in mind as we start to work with dictionaries and sets. Some immutable objects. numbers (integers, floating-point numbers, complex numbers) strings. tuples. booleans “frozen”-sets. Some mutable objects ...
The Walrus Operator in Python is a new assignment operator which is introduced in Python version 3.8 and higher. This operator is used to assign a value to a variable within an expression. Syntax: a := expression. Example: In this code, we have a Python list of integers. We have used Python Walrus assignment operator within the Python while loop.
You assign variables with an equals sign (=). In Python, a single equals sign = is the “assignment operator.” (A double equals sign == is the “real” equals sign.) Variables are names for values. In Python the = symbol assigns the value on the right to the name on the left. The variable is created when a value is assigned to it.
Python doesn't use the pass-by-value model, nor the pass-by-reference one; Python uses a pass-by-assignment model (using “nicknames”); each object is characterised by. its identity; its type; and. its contents. the id function is used to query an object's identifier; the type function is used to query an object's type;
Python has two distinct types of change. Assignment changes a variable. That is, it changes which object a variable points to. Mutation changes an object, which any number of variables may be pointing to. So we can change variables through an assignment, and we can change the objects that those variables point to through a mutation.
Python Assignment Operators. Assignment operators are used to assign values to variables: Operator. Example. Same As. Try it. =. x = 5. x = 5.