Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Object-Oriented Python Advanced Objects Subclassing Built-ins

Robert Baucus
Robert Baucus
3,400 Points

Subclassing Built-Ins, why are we using copy.copy ?

At a certain point in the video Kenneth explains why he is using copy.copy in the following function: (from the transcript:)


Now why am I using copy.copy? Well, if they send in something mutable, like say, another list, each member in that filled list or each member in our filled list that was a copy of that list would be the same member if we weren't using copy.copy. If you changed one of them it would change all of the others too because we're just putting in references to that list.


#example he is talking about in the transcript:
import copy
class FilledList(list):
    def __init__(self, count, value, *args, **kwargs):
        super().__init__()
        for _ in range(count):
            self.append(copy.copy(value))

I can't follow what he is talking about. Could someone please shed some light on this?

2 Answers

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 68,457 Points

Great question! In python, objects are referenced by their location in memory. This location can be seen using the id() function.

When appending an item to a list, it is the id of the item that is append, not the actual item.

# set an initial value
>>> value = [1, 2, 3]
>>> result = []
>>> for _ in range(5):
...     result.append(value)
...
>>> for item in result:
...     print(id(item))
...
18867400
18867400
18867400
18867400
18867400
# All of the IDs are the same
>>> print( result)
[[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]
# because the IDs are all the same, modifying one is the same
# as modifying all since the all point to the same object id
>>> result[0][1] = 9
>>> print( result)
[[1, 9, 3], [1, 9, 3], [1, 9, 3], [1, 9, 3], [1, 9, 3]]

# Trying again using copy
>>> import copy
# reset result
>>> result = []
>>> for _ in range(5):
...     result.append(copy.copy(value))
...
>>> print( result)
[[1, 9, 3], [1, 9, 3], [1, 9, 3], [1, 9, 3], [1, 9, 3]]
# This looks the same, but check out the IDs
>>> for item in result:
...     print(id(item))
...
18908872
18909640
18909768
18909064
18909832
# All different!!
# now modifying one is only changing that one
>>> result[0][1] = 'a'
>>> print( result)
[[1, 'a', 3], [1, 9, 3], [1, 9, 3], [1, 9, 3], [1, 9, 3]]

The same situation occurs when mutable objects are passed as arguments to functions or methods. This means that a function can change values outside of the function if the coding is not done carefully.

>>> my_list = [1, 2, 3, 4, 5]
>>> def double_list(lst):
...     for idx, item in enumerate(lst):
...         lst[idx] = item * 2
...     return lst
...
>>> double_list(my_list)
[2, 4, 6, 8, 10]
>>> my_list
# my_list is also changed outside of function!
[2, 4, 6, 8, 10]

The fix for the above situation would be to append to a different list and return that list. Or you could add the line:

    # Add line near top of function 
    lst = copy.copy(lst)

Post back if you have more questions. Good luck!!

Robert Baucus
Robert Baucus
3,400 Points

Excellent and fascinating ! Thanks