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 Introducing Lists Using Lists Mutability

Too difficult to understand almost watched 4 to 5 times but can't understand

Can any one can help me what they said in this video.

2 Answers

Jeff Muday
MOD
Jeff Muday
Treehouse Moderator 28,716 Points

Craig @CraigDennis is demonstrating 2 important principles in this video:

  • refactoring
  • mutability of lists

Refactoring

The first is "refactoring" to remove repeated code and improve modularity. This step recognizes a "repeating design pattern" and rewrites it into a separate function. This is a great way to write maintainable code and save us from having to repeat ourselves. In Python we try to follow the "DRY" principle. DRY stands for "Don't Repeat Yourself". There are many good reasons for this-- for me, I like to solve a problem once and use the solution many times over.

Mutability of lists

When we pass a list into a function, we are not passing in a copy of the list, but the list itself. So if we make changes to the list it stays changed and you can't go back to the original list. In this case using "pop()", the list is changed/mutated permanently. One way to solve this is by making a copy of the list inside the function. A list can be copied by using a copy method or a special slice.

list2 = list1 # not good... list1 and list2 point to the same memory structure!
list2 = list1.copy() # good, list2 is a NEW copy of the list.
list2 = list1[:] # this is good, list2 is a NEW copy of the list (using slice notation)

Note, we can check the id of a list and if the ids of two lists match, they both reference the same memory structure!

Python 3.6.2 (v3.6.2:5fd33b5, Jul  8 2017, 04:14:34) [MSC v.1900 32 bit (Intel)] on win32
> # Example 1, direct assignment of list1 to list2 yields the same id (the same memory object)
> list1 = ['Apple', 'Banana', 'Orange']
> id(list1)
59022592
> list2 = list1
> list2
['Apple', 'Banana', 'Orange']
> id(list2)
59022592
> list2.pop()
'Orange'
> list2
['Apple', 'Banana']
> list1
['Apple', 'Banana']

> # Example 2 now let's try that again with a copy
> list1 = ['Apple', 'Banana', 'Orange']
> id(list1)
58831776
> list2 = list1.copy()
> id(list2)
57859448
list2
['Apple', 'Banana', 'Orange']
> list2.pop()
'Orange'
> list1
['Apple', 'Banana', 'Orange']

Not all Python data types are mutable. This is an intermediate-level question you will come to later in Python, for now, Craig is trying to keep it simple and teach you good rules to follow in designing code.

Best of luck on your Python journey!

Thanks to guide me. I am very thank full of you.

Abhishek Kulkarni
Abhishek Kulkarni
4,405 Points

But if we keep using copies of many lists throughout our program, are we not taking extra memory than needed?

Maya Husic
Maya Husic
13,488 Points

Thank you for this thorough explanation as well as the 'garbage collection' bellow, really helps with grasping some of these concepts better!

Jeff Muday
MOD
Jeff Muday
Treehouse Moderator 28,716 Points

Python has automated "garbage collection" to handle freeing up of memory when a list (or other data structure) is no longer in the current execution scope. For every object, there is an internal Python "reference counter." When the reference counter is 0, then garbage collection will "release" the memory block to the free pool of memory.

Below, list2 is instantiated as a copy when my_function is called. The reference counter to list2 becomes zero (flagged for deletion) when my_function is done executing. list1 is possibly MODIFIED in the process (because we explicitly remove the non-even items), and thus the list is considered "mutated". Note, that we could not properly loop over list1 since the "for" loop doesn't (properly) keep track of a list that becomes mutated by addition or removal of elements.

def count_evens(list1):
    """function removes non-even numbers from list1 and returns a count of how many were removed"""
    list2 = list1[:]
    count = 0
    for item in list2:
        if item % 2:
            list1.remove(item)
            count += 1
    return count

A programmer can also practice deletion of a list copy when it is no longer needed in the current scope. Note that if there are still references to list2, even though it is explicitly deleted with "del" operator, it will remain as long as the reference counter is greater than zero

list1 = [a,b,c,d]
list2 = list1[:]
del list2 # sets reference counter to zero, so will be cleaned up "immediately"