How To Use Lists in Python

Lists in Python
(Image credit: Tom's Hardware)

Lists are a common way to store multiple pieces of data in Python. By creating one list we can neatly store many different types of data within it. They work just like a shopping list. We give the list a name, then enter items under the list. 

Lists are another data storage structure, just like dictionaries, tuples and sets. Lists are mutable objects, we can change the contents by updating, deleting and adding to a list. Objects stored in a list come under the broad term of “items” and each item has a position in the list, an index number. The first object in the list has the index number of zero, and as we get more items, it counts up.

Latest Videos FromTom's Hardware
Les Pounder
Associate Editor

Les Pounder is an associate editor at Tom's Hardware. He is a creative technologist and for seven years has created projects to educate and inspire minds both young and old. He has worked with the Raspberry Pi Foundation to write and deliver their teacher training program "Picademy".

  • Li Ken-un
    3. Use extend to add multiple items to the shopping list. Extend requires us to wrap the items that we wish to add in parenthesis. These new items can be any iterable objects, in this case I have used a tuple to contain the items.
    shopping.extend(("Butter","Eggs"))
    The first part of the description could use a better explanation. Wrapping the items in parentheses creates a tuple. That’s not a syntactic detail. If you gave it an Iterable instead, wrapping it in parentheses is redundant.

    This code:
    the_list = # Empty list

    the_list.append('First item')

    the_list.extend() # Extending the List with a List literal

    fifth_item_and_sixth_items = ('Fifth item', 'Sixth item') # Assigning a Tuple to a variable

    the_list.extend(fifth_item_and_sixth_items) # Extending the List with a Tuple variable

    seventh_to_ninth_items = # Assigning a List to a variable

    the_list.extend(seventh_to_ninth_items) # Extending the List with a List variable

    the_list.extend(('Tenth item',)) # Extending the List with a Tuple literal containing only one item

    randomly_ordered_set = {'A', 'B', 'C', 1, 2, 3} # Assigning a Set to a variable; items will be unique and unordered

    the_list.extend(randomly_ordered_set)

    sorted_list = sorted(map('Sorted item: {!r}'.format, randomly_ordered_set)) # Assigning a List to a variable created from the randomly ordered set above

    the_list.extend(sorted_list)

    print('The list items:')
    print('\n'.join(map('* {}'.format, the_list)))
    Yields this output (may differ where Sets are used):
    The list items:
    * First item
    * Second item
    * Third item
    * Fourth item
    * Fifth item
    * Sixth item
    * Seventh item
    * Eighth item
    * Ninth item
    * Tenth item
    * A
    * 1
    * 2
    * 3
    * B
    * C
    * Sorted item: 'A'
    * Sorted item: 'B'
    * Sorted item: 'C'
    * Sorted item: 1
    * Sorted item: 2
    * Sorted item: 3
    Reply
  • bit_user
    I think the biggest omission is probably that you can use a list to pass a variable into a function, as a means of pass-by-reference. Here's a quick, hypothetical example:

    num_fruits = 0
    num_vegetables = 0
    num_grains = 0

    for bag in groceries:
    update_counts( bag, num_fruits, num_vegetables, num_grains )
    At the end, all of the counters will still be zero, because any updates the function makes to those parameter values will be local and won't reach the scope of the caller. If you instead passed in a list of counters, then the function could modify the list and the caller would see the modifications.

    The other key thing is to show how to force a copy, when you don't want to simply copy or pass a reference.

    I also think it was a missed opportunity to omit any mention of built-in functions that operate on iterable objects (which includes lists):
    all()
    any()
    enumerate()
    filter()
    map()
    max()
    min()
    sorted()
    sum()
    zip()
    For details, look here: https://docs.python.org/3/library/functions.html
    Reply
  • Li Ken-un
    bit_user said:
    I think the biggest omission is probably that you can use a list to pass a variable into a function, as a means of pass-by-reference.
    This is a behavior of Python I’ve never looked into, but a bit of experimentation yielded some interesting results.

    Everything in Python is actually already passed by reference. 😯
    def function(value):
    print(f'The memory address of the argument is {id(value):x}.')

    letter = 'A'
    number = 1
    mutable_list =
    print(f'The memory address of the letter is {id(letter):x}.')
    print(f'The memory address of mutable_list is {id(mutable_list):x}.')
    function(letter)

    print()

    print(f'The memory address of the number is {id(number):x}.')
    print(f'The memory address of mutable_list is {id(mutable_list):x}.')
    function(number)

    print()

    print(f'The memory address of mutable_list is {id(mutable_list):x}.')
    function(mutable_list)Output:The memory address of the letter is a64740.
    The memory address of mutable_list is a64740.
    The memory address of the argument is a64740.
    The memory address of the number is a59a68.
    The memory address of mutable_list is a59a68.
    The memory address of the argument is a59a68.
    The memory address of mutable_list is 7fa219398e00.
    The memory address of the argument is 7fa219398e00.
    But yea, assigning to the argument isn’t a way to get the result outside the function:
    def arg_mutating_function_1(arg):
    print(f'\tThe memory address of arg in the function is {id(arg):x}.')
    print(f'\tThe value of arg in the function is {arg!r}')
    arg += 1
    print(f'\tThe value of arg in the function is {arg!r}')
    print(f'\tThe memory address of arg in the function is {id(arg):x}.')

    number = 0
    print(f'The memory address of number is {id(number):x}.')
    print(f'The value of number is {number!r}')
    arg_mutating_function_1(number)
    print(f'The value of number is {number!r}')
    print(f'The memory address of number is {id(number):x}.')Output:The memory address of number is a59a48.
    The value of number is 0
    The memory address of arg in the function is a59a48.
    The value of arg in the function is 0
    The value of arg in the function is 1
    The memory address of arg in the function is a59a68.
    The value of number is 0
    The memory address of number is a59a48.
    Putting the variable of interest in a List does allow for it:
    def arg_mutating_function_2(arg):
    print(f'\tThe memory address of arg in the function is {id(arg):x}.')
    print(f'\tThe value of arg in the function is {arg!r}')
    arg += 1
    print(f'\tThe value of arg in the function is {arg!r}')
    print(f'\tThe memory address of arg in the function is {id(arg):x}.')

    number_in_list = print(f'The memory address of number_in_list is {id(number_in_list):x}.')
    print(f'The value of number_in_list is {number_in_list!r}')
    arg_mutating_function_2(number_in_list)
    print(f'The value of number_in_list is {number_in_list!r}')
    print(f'The memory address of number is {id(number_in_list):x}.')Output:The memory address of number_in_list is 7fa21822e7c0.
    The value of number_in_list is The memory address of arg in the function is 7fa21822e7c0.
    The value of arg in the function is The value of arg in the function is The memory address of arg in the function is 7fa21822e7c0.
    The value of number_in_list is The memory address of number is 7fa21822e7c0.
    Wrapping in anything pretty much allows for it as well:
    from dataclasses import dataclass

    @dataclass
    class number_wrapper:
    number: int

    def arg_mutating_function_3(arg):
    print(f'\tThe memory address of arg in the function is {id(arg):x}.')
    print(f'\tThe value of arg in the function is {arg!r}')
    arg.number += 1
    print(f'\tThe value of arg in the function is {arg!r}')
    print(f'\tThe memory address of arg in the function is {id(arg):x}.')

    number_wrapping_instance = number_wrapper(0)
    print(f'The memory address of number_wrapping_instance is {id(number_wrapping_instance):x}.')
    print(f'The value of number_wrapping_instance is {number_wrapping_instance!r}')
    arg_mutating_function_3(number_wrapping_instance)
    print(f'The value of number_wrapping_instance is {number_wrapping_instance!r}')
    print(f'The memory address of number_wrapping_instance is {id(number_wrapping_instance):x}.')Output:The memory address of number_wrapping_instance is 7fa2193fc250.
    The value of number_wrapping_instance is number_wrapper(number=0)
    The memory address of arg in the function is 7fa2193fc250.
    The value of arg in the function is number_wrapper(number=0)
    The value of arg in the function is number_wrapper(number=1)
    The memory address of arg in the function is 7fa2193fc250.
    The value of number_wrapping_instance is number_wrapper(number=1)
    The memory address of number_wrapping_instance is 7fa2193fc250.

    Manipulating arguments this way is sort of like using C♯’s ref or out keywords, but doesn’t feel idiomatic in Python. The most common example of C♯’s ref or out is bool TryParse(string? s, out T result) which returns whether it parsed successfully and assigns the result of type T as a side-effect. In Python, returning a Tuple is the closest idiomatic equivalent. But now that the match statement is available in Python there’s an elegant way to handle the returned Tuple:
    def try_parse(string: str) -> (bool, int):
    try:
    return True, int(string)
    except:
    return False, None

    def test(string: str) -> None:
    match try_parse(string):
    case True, value:
    print(f'Parsed string {string!r} into value {value!r}.')
    case _:
    print(f'Failed to parse string {string!r}.')

    for string in ('123456789', 'lol no', '1.2'):
    test(string)Output:Parsed string '123456789' into value 123456789.
    Failed to parse string 'lol no'.
    Failed to parse string '1.2'.
    Of course the above is a contrived example. It’s idiomatic Python to use exception handling as part of normal control flow. “Easier to ask for forgiveness than permission.”
    bit_user said:
    zip()
    zip is quite the fun function. 😬
    Reply
  • bit_user
    Li Ken-un said:
    This is a behavior of Python I’ve never looked into, but a bit of experimentation yielded some interesting results.
    Thanks for posting your test code. Illustrates some key points.

    Li Ken-un said:
    Everything in Python is actually already passed by reference. 😯
    I meant semantically, not mechanically. Since Python uses garbage collection, it doesn't surprise me if even immutable types are passed by reference.

    Li Ken-un said:
    now that the match statement is available in Python there’s an elegant way to handle the returned Tuple:
    Thanks for the demo. I hadn't heard of match, but then I really don't follow Python, lately.
    Reply