Resource linked in this lesson: Python Sequences: A Comprehensive Guide
Exploring Copy Behavior in Python
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.