News How To Use Lists in Python

Status
Not open for further replies.
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.
Python:
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:
Python:
the_list = [] # Empty list

the_list.append('First item')

the_list.extend(['Second item', 'Third item', 'Fourth item']) # 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 = ['Seventh item', 'Eighth item', 'Ninth item'] # 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
 
Last edited:
  • Like
Reactions: 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:

Python:
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
 
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. 😯
Python:
def function(value):
    print(f'The memory address of the argument is {id(value):x}.')

letter = 'A'
number = 1
mutable_list = [letter, number]

print(f'The memory address of the letter is {id(letter):x}.')
print(f'The memory address of mutable_list[0] is {id(mutable_list[0]):x}.')
function(letter)

print()

print(f'The memory address of the number is {id(number):x}.')
print(f'The memory address of mutable_list[1] is {id(mutable_list[1]):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[0] is a64740.
The memory address of the argument is a64740.
The memory address of the number is a59a68.
The memory address of mutable_list[1] 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:
Python:
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:
Python:
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[0] += 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 = [0]
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 [0]
The memory address of arg in the function is 7fa21822e7c0.
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 7fa21822e7c0.
The value of number_in_list is [1]
The memory address of number is 7fa21822e7c0.
Wrapping in anything pretty much allows for it as well:
Python:
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:
Code:
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.”

zip is quite the fun function. 😬
 
  • Like
Reactions: bit_user
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.

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.

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.
 
Status
Not open for further replies.