Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Exploring Copy Behavior in Python

Resource linked in this lesson: Python Sequences: A Comprehensive Guide

00:00 There’s more to creating copies than just the copy and deepcopy() functions of the copy module. Let’s look at some other common techniques for creating copies in Python.

00:10 You can call certain built-in class constructors. For example, list_copy equals the result of passing original_list into the list constructor.

00:19 You can use Python slice syntax for sequences. An example would be list_copy equals original_list with a slice containing a single colon, or you can invoke the copy() method of some Python containers.

00:33 An example of this would be list_copy equals calling original_list.copy(). And of course these methods apply to much more than Python lists.

00:43 So let’s take a look at examples of each of these techniques.

00:48 You’ll start by defining a few different containers. A list called fruits, holding the strings, “apple” and “banana.”

00:57 A dictionary called dairy holding the key value pairs “cheese” 15 and “milk” 20,

01:05 and a set called seafood with the strings, “shrimp” and “salmon”. Using their respective constructors, you can make copies of each: fruits_copy equals list(fruits), dairy_ copy equals dict(dairy) and seafood_ copy equals set(seafood).

01:27 To see that these are copies and not aliases, use the is operator to compare identities as well as the == operator to compare equality. fruits_ copy is fruits, fruits_copy == fruits.

01:44 dairy_copy is dairy, dairy_copy == dairy.

01:51 seafood_copy is seafood, seafood_copy == seafood.

01:59 All three return the tuples of False and True meaning that they are distinct objects, but they are equal to the originals. An interesting quirk of this approach is that when using an immutable type, like a tuple, Python will create an alias and not a copy.

02:15 Don’t believe me? First, create a tuple veggies containing the strings “carrot” and “beans”, and create a copy using the tuple() constructor veggies_ copy = tuple(veggies).

02:31 Compare using the is operator veggies_copy is veggies. Returns True. Python behaves this way out of efficiency. Since the object is immutable, unchanging, there’s no need to create a new object as a copy.

02:46 This can be applied to a wide range of constructors as well.

02:51 Here’s a useful table showing the most common class constructors and their copy behavior. As mentioned, for immutable types, the behavior is to not copy. For mutable types, however, shallow copies are created.

03:06 Clear the screen and move on to slicing. Slicing can be thought of as an extension of accessing elements in a sequence using square brackets but returning a subsequence instead of a single element.

03:17 It works for any ordered sequence, like lists, tuples, or strings. Define the list fruits again with four strings: “apple,” “banana,” “orange,” and “grape;” fruits = the list ["apple", "banana", "orange", "grape"].

03:32 To slice, you pass up to three optional parameters into square brackets on the list, separated by colons. These correspond to the start position, non-inclusive stop position, and the step size.

03:45 A quick example: fruits[1:3] returns the second and third elements of the list, the items at index 1 and 2, which are “banana” and “orange.” How does this relate to copying?

03:59 The default behavior for a slice is to start from the first element and run to and include the last element returning what is a full copy of the sequence.

04:08 You can take advantage of this by including only a single colon in the slice like this. fruits[:] returns “apple,” “banana,” “orange,” and “grape.” The full list. So is it a copy or an alias?

04:21 You know what to do. Use the is operator to check.

04:26 fruits[:] is fruits returns False. And just like with class constructors, Python will alias instead of copying when you apply this to immutable containers.

04:36 Create a tuple from fruits by passing fruits into the tuple constructor and call it fixed_fruits.

04:44 Using is, fixed_fruits[:] is fixed _fruits returns True. And we’ve only talked about creating copies, but there’s a lot more to slicing as well as other sequence operations.

04:56 So if you’d like to study up, here’s another tutorial for you to check out, Python Sequences: A Comprehensive Guide. Don’t run off though. We have one more copy technique to look at.

05:07 Some sequence types have a copy() method and you’ll most often be using it with lists and dictionaries. Using the same fruits list, you can try fruits. copy(), which returns a copy of the list: “apple,” “banana,” “orange,” and “grape.” One last time, confirm that it’s not an alias using is, fruits.copy() is fruits, returns False. Perfect.

05:30 So which of these techniques should you use? It’s largely a matter of individual preference, and it’s always best to be consistent within the code base where your code will be used.

05:40 In general though, if the object you’re copying has a copy() method, that will tend to be the most efficient. Though the performance gains are mainly significant on sequences with fewer elements.

05:51 Class constructors are useful, especially when you need to convert a sequence from one type to another. But on the other hand, slicing can be more versatile since it works for any Python container type that supports slice notation.

06:03 And then of course there are the copy() and deepcopy() functions from the copy module, which are particularly useful when dealing with user-defined types where you the programmer can take full control of how they behave.

06:14 So if that kind of mastery appeals to you, well, you are going to want to stick around for the next couple lessons.

Become a Member to join the conversation.