Contents

Python Quick Guide - Step 1 Basic Syntax and Data Types (6) - Lists (2)

Info
  • This course is designed to help you learn the basics of Python programming as quickly as possible through hands-on practice.
  • The “Style Guide” sections primarily cover guidelines from PEP8 for writing clean Python code.
  • You can run and see the results of each code example.
    Feel free to experiment with the code - reloading the page will reset the content.

This is a continuation of “Step 1: Basic Syntax and Data Types”.

  • To check if an element exists in a list, use the in operator.
  • The not in operator performs the opposite check of the in operator.
  • You can get a subset of a list by specifying a range as [start:end].
    • This subset is called a slice.
    • The slice includes elements from start up to but not including end (start <= x < end).
    • Note that the element at the end index is not included.
  • If the specified range exceeds the actual list range,
    no error occurs and it returns a slice containing only elements within the actual range.
    • If there are no values in the specified range, an empty list ([]) is returned.
    • With slices, specifying an out-of-range index does not raise an IndexError.
  • If you omit the start position ([:end]), the slice begins from the start of the list.
  • If you omit the end position ([start:]), the slice continues to the end of the list.
  • You can specify [start:end:step] to get a slice with elements at intervals of step.
  • Specifying a negative value for step creates a reversed slice.
    • Since it proceeds in reverse order, the slice includes elements where start >= x > end (the element at end is not included in the slice).
  • With a negative step:
    • If you omit the start position, the slice begins from the end of the list.
    • If you omit the end position, the slice continues to the beginning of the list.
📚Exercise

Using a list containing numbers 1 to 20, perform the following:

  1. Output a slice containing numbers 5 to 15.
  2. Output a slice containing only multiples of 5.
  3. Output a reversed slice containing only multiples of 3.
  • You can replace elements in a list using slices.
  • Be careful as you can assign lists of different sizes without causing errors.

In “1.4. Variables and Assignment”, we explained that variables are like “labels”. Here, we’ll demonstrate a common mistake in Python that illustrates how variables work as labels.

As you can see from running the above code, the original price list (original_prices) has also changed. This can be understood by thinking of variables as “labels”.

graph TD
    subgraph "Step 3: Modifying an Element"
        list3["[500, 1500, 5000]"]
        original_prices3[original_prices] --> list3
        new_prices3[new_prices] --> list3
    end
    
    subgraph "Step 2: Assigning to a New Variable"
        list2["[1000, 1500, 5000]"]
        original_prices2[original_prices] --> list2
        new_prices2[new_prices] --> list2
    end

    subgraph "Step 1: Initial State"
        list1["[1000, 1500, 5000]"]
        original_prices1[original_prices] --> list1
    end
    
    style list1 fill:#fff3bf,stroke:#333,stroke-width:2px
    style list2 fill:#fff3bf,stroke:#333,stroke-width:2px
    style list3 fill:#fff3bf,stroke:#ff0000,stroke-width:2px

In Python, when you assign a variable to a new variable, the new variable doesn’t get a copy of the data. Instead, the new variable becomes another label pointing to the original data. (In other words, both the original variable and the new variable refer to the same underlying data.)

  • To copy a list, use the copy() method.
graph TB
    %% Step 1: Initial State
    subgraph step1["Step 1: Initial State"]
        direction TB
        op1[original_prices] --> list1["[1000, 1500, 5000]"]
    end
    
    step1 --> step2
    
    %% Step 2: After copy() method
    subgraph step2["Step 2: Copy the list using copy() method"]
        direction TB
        op2[original_prices] --> list2["[1000, 1500, 5000]"]
        list2 -. "copy()" .-> list2_copy["[1000, 1500, 5000] (copy)"]
        np2[new_prices] --> list2_copy
    end
    
    step2 --> step3
    
    %% Step 3: After modification
    subgraph step3["Step 3: After modifying an element in the new list"]
        direction TB
        op3[original_prices] --> list3["[1000, 1500, 5000]"]
        np3[new_prices] --> list3_copy["[500, 1500, 5000]"]
    end
    
    %% Style settings
    style list1 fill:#fff3bf,stroke:#333,stroke-width:2px
    style list2 fill:#fff3bf,stroke:#333,stroke-width:2px
    style list3 fill:#fff3bf,stroke:#333,stroke-width:2px
    style list2_copy fill:#fff3bf,stroke:#333,stroke-width:2px
    style list3_copy fill:#fff3bf,stroke:#ff0000,stroke-width:2px
    linkStyle 3 stroke:#4285F4,stroke-width:2px
  • You can also copy a list using the slice notation with omitted start and end positions [:].

However, there are cases where even the copy() method or the [:] slice can cause problems. Here’s an example:

This happens because the copy() method or the [:] slice only duplicates the outer list ([..., ..., ...]), but the inner values (["apple", 120], etc.) are not duplicated and still reference the same values as the original list. This type of copy is called a “shallow copy”.

graph TB
    %% Step 1: Initial State
    subgraph step1["Step 1: Initial State"]
        direction TB
        np1[name_and_prices] --> outer1["[..., ..., ...]"]
        outer1 --> apple1["['apple', 120]"]
        outer1 --> orange1["['orange', 150]"]
        outer1 --> grape1["['grape', 180]"]
    end
    
    step1 --> step2
    
    %% Step 2: After copy() method
    subgraph step2["Step 2: Copy the list using copy() method"]
        direction TB
        np2[name_and_prices] --> outer2["[..., ..., ...]"]
        nnp2[new_name_and_prices] --> outer2_copy["[..., ..., ...] (copy)"]
        
        outer2 --> apple2["['apple', 120]"]
        outer2 --> orange2["['orange', 150]"]
        outer2 --> grape2["['grape', 180]"]
        
        outer2_copy --> apple2
        outer2_copy --> orange2
        outer2_copy --> grape2
    end
    
    step2 --> step3
    
    %% Step 3: After modification
    subgraph step3["Step 3: After modifying an element inside a list element"]
        direction TB
        np3[name_and_prices] --> outer3["[..., ..., ...]"]
        nnp3[new_name_and_prices] --> outer3_copy["[..., ..., ...] (copy)"]
        
        outer3 --> apple3["['apple', 120]"]
        outer3 --> orange3["['orange', 200]"]
        outer3 --> grape3["['grape', 180]"]
        
        outer3_copy --> apple3
        outer3_copy --> orange3
        outer3_copy --> grape3
    end
    
    %% Style settings
    style outer1 fill:#fff3bf,stroke:#333,stroke-width:2px
    style outer2 fill:#fff3bf,stroke:#333,stroke-width:2px
    style outer3 fill:#fff3bf,stroke:#333,stroke-width:2px
    style outer2_copy fill:#fff3bf,stroke:#333,stroke-width:2px
    style outer3_copy fill:#fff3bf,stroke:#333,stroke-width:2px
    
    style apple1 fill:#ffc078,stroke:#333,stroke-width:2px
    style orange1 fill:#ffc078,stroke:#333,stroke-width:2px
    style grape1 fill:#ffc078,stroke:#333,stroke-width:2px
    style apple2 fill:#ffc078,stroke:#333,stroke-width:2px
    style orange2 fill:#ffc078,stroke:#333,stroke-width:2px
    style grape2 fill:#ffc078,stroke:#333,stroke-width:2px
    style apple3 fill:#ffc078,stroke:#333,stroke-width:2px
    style orange3 fill:#ffc078,stroke:#ff0000,stroke-width:2px
    style grape3 fill:#ffc078,stroke:#333,stroke-width:2px
  • When you replace an entire element (list) instead of modifying elements inside it, it works as a “reassignment of labels” as explained in “1.4.3. Variable Reassignment”, so it doesn’t affect the original list.
graph TB
    %% Step 1: Initial State
    subgraph step1["Step 1: Initial State"]
        direction TB
        np1[name_and_prices] --> outer1["[..., ..., ...]"]
        outer1 --> apple1["['apple', 120]"]
        outer1 --> orange1["['orange', 150]"]
        outer1 --> grape1["['grape', 180]"]
    end
    
    step1 --> step2
    
    %% Step 2: After copy() method
    subgraph step2["Step 2: Copy the list using copy() method"]
        direction TB
        np2[name_and_prices] --> outer2["[..., ..., ...]"]
        nnp2[new_name_and_prices] --> outer2_copy["[..., ..., ...] (copy)"]
        
        outer2 --> apple2["['apple', 120]"]
        outer2 --> orange2["['orange', 150]"]
        outer2 --> grape2["['grape', 180]"]
        
        outer2_copy --> apple2
        outer2_copy --> orange2
        outer2_copy --> grape2
    end
    
    step2 --> step3
    
    %% Step 3: After replacing an element
    subgraph step3["Step 3: After replacing an entire element in the new list"]
        direction TB
        np3[name_and_prices] --> outer3["[..., ..., ...]"]
        nnp3[new_name_and_prices] --> outer3_copy["[..., ..., ...] (copy)"]
        
        outer3 --> apple3["['apple', 120]"]
        outer3 --> orange3["['orange', 150]"]
        outer3 --> grape3["['grape', 180]"]
        
        outer3_copy --> apple3
        outer3_copy --> orange3
        outer3_copy -. "before replacement" .-> grape3
        outer3_copy -- "after replacement" --> banana3["['banana', 200]"]
    end
    
    %% Style settings
    style outer1 fill:#fff3bf,stroke:#333,stroke-width:2px
    style outer2 fill:#fff3bf,stroke:#333,stroke-width:2px
    style outer3 fill:#fff3bf,stroke:#333,stroke-width:2px
    style outer2_copy fill:#fff3bf,stroke:#333,stroke-width:2px
    style outer3_copy fill:#fff3bf,stroke:#333,stroke-width:2px
    
    style apple1 fill:#ffc078,stroke:#333,stroke-width:2px
    style orange1 fill:#ffc078,stroke:#333,stroke-width:2px
    style grape1 fill:#ffc078,stroke:#333,stroke-width:2px
    style apple2 fill:#ffc078,stroke:#333,stroke-width:2px
    style orange2 fill:#ffc078,stroke:#333,stroke-width:2px
    style grape2 fill:#ffc078,stroke:#333,stroke-width:2px
    style apple3 fill:#ffc078,stroke:#333,stroke-width:2px
    style orange3 fill:#ffc078,stroke:#333,stroke-width:2px
    style grape3 fill:#ffc078,stroke:#333,stroke-width:2px
    style banana3 fill:#ffc078,stroke:#ff0000,stroke-width:2px
📚Exercise

Think about what would happen if we ran the above code without using the copy() method.

Shallow Copy and Deep Copy

In contrast to the shallow copy described above, a copy that completely duplicates all elements, including nested elements, is called a “deep copy”.

You can perform a deep copy using the deepcopy function from the copy library: (We’ll explain more about “libraries” later)

  • If you want to assign each element of a list to a separate variable, you can write variables separated by commas (,) on the left side of the equals sign (=).
    • This operation is called unpacking.
  • If the number of variables doesn’t match the number of elements, a ValueError occurs.
  • Using the unpacking operator *, you can unpack elements into “the first element” and “the rest”. (This * is different from the multiplication operator; it’s placed before a variable name.)
  • Using multiple unpacking operators * in a single unpacking operation raises a SyntaxError.
  • When unpacking, you can use an underscore (_) to receive values you don’t need.
    • This is a Python convention to explicitly indicate that you’re not using that value.
  • When the bool function is applied to a list, it returns False if the list is empty ([]), and True otherwise.

Related Content