Spread operator in Python? How to unpack list/dict

eye-catch Python

I often used spread operator in TypeScript/JavaScript and I needed to do it in Python. The spread operator in TypeScript/JavaScript is ... while it’s an asterisk * in Python. Let’s check how it works.

Unpacking one dimensional List

Let’s start with a simple example. Prepending an asterisk to the variable that we want to unpack.

array1 = [1, 2, 3, 4]
array2 = [9, 8, 7, 6]
array_of_array = [array1, array2]
flatten_array = [*array1, *array2]

print(array_of_array)  # [[1, 2, 3, 4], [9, 8, 7, 6]]
print(flatten_array)  # [1, 2, 3, 4, 9, 8, 7, 6]

The items are correctly unpacked.

Unpacking two dimensional List

The asterisk unpacks only the first level. If the nested list needs to be flattened, we need another way.

print(*array_of_array)  # [1, 2, 3, 4] [9, 8, 7, 6]
print(*flatten_array)  # 1 2 3 4 9 8 7 6

flatten_array2 = [
    *array_of_array[0],
    *array_of_array[1],
]
print(flatten_array2)  # [1, 2, 3, 4, 9, 8, 7, 6]

Add list to a list

If both two variables are list, we can use plus operator. If the original variable needs to be updated, extend is also available.

print(array_of_array[0] + array_of_array[1])  # [1, 2, 3, 4, 9, 8, 7, 6]
array_of_array[0].extend(array_of_array[1])
print(array_of_array[0])  # [1, 2, 3, 4, 9, 8, 7, 6]

But beware of the fact that the referenced value is updated. The array_of_array was created in the following way.

array1 = [1, 2, 3, 4]
array2 = [9, 8, 7, 6]
array_of_array = [array1, array2]
flatten_array = [*array1, *array2]

An asterisk is not used for it. It means that the reference of array1 is used there. If we access array_of_array[0], it accesses array1. In other words, all operations for array_of_array[0] affect the original variable array1.

See the following result.

print(array1)  # [1, 2, 3, 4, 9, 8, 7, 6]
print(flatten_array)  # [1, 2, 3, 4, 9, 8, 7, 6]
print(flatten_array2)  # [1, 2, 3, 4, 9, 8, 7, 6]

array1 is accidentally updated. flatten_array and flatten_array2 are created by using an asterisk, they are not affected because the values are copied from the original variable. It’s not a reference.

Use asterisk to pass parameters to a function

A function might require parameters separately while we have a list of data. We can use an asterisk to pass the parameters in this case.

def calc_sum(a, b, c, d):
    print(f"sum({a}, {b}, {c}, {d}):{a+b+c+d}")


calc_sum(*[1, 2, 3, 4])  # sum(1, 2, 3, 4):10

An error occurs if the list has more items than the required number of parameters. In this case, pass only the required number of parameters by using a colon for example.

params = [1, 2, 3, 4, 5]
# Too many positional arguments for function call
# calc_sum(*params)  
calc_sum(*params[0:4])  # sum(1, 2, 3, 4):10
calc_sum(*params[1:5])  # sum(2, 3, 4, 5):14
calc_sum(*params[-4:])  # sum(2, 3, 4, 5):14

Check the following post too for the list with a colon if you want to know more about it.

Two asterisks are needed for Dictionary

If an asterisk is used for a dictionary, only keys are extracted. It’s similar to keys() method.

data_dict = {
    "one": 1,
    "two": 2,
    "three": 3,
}
print(*data_dict)  # one two three
print(" ".join(data_dict.keys()))  # one two three

If it’s assigned to a list, it can be used in the same way as keys() but if it’s assigned with {}, the data type will be set.

array_from_dict = [*data_dict]
set_from_dict = {*data_dict}
print(array_from_dict) # ['one', 'two', 'three']
print(isinstance(set_from_dict, set))  # True
print(set_from_dict)  # {'one', 'three', 'two'}
print(set_from_dict.pop()) # one

I guess we rarely implement it in this way to use keys. It’s better to use keys() method in this case for readability.

To create a new dict from a dict, two asterisks are needed.

dict_from_dict = {**data_dict}
print(isinstance(dict_from_dict, dict))  # True
# {'one': 1, 'two': 2, 'three': 33, 'four': 4}
print({**dict_from_dict, "four": 4, "three": 33})
# {'four': 4, 'three': 3, 'one': 1, 'two': 2}
print({"four": 4, "three": 33, **data_dict})

As you can see, if the same key appears in the same dict, the last one is used.

Flatten a nested list

Do you need to flatten a nested list? Using an asterisk is not enough in this case.

nested_arrays = [[1, 2, 3], [4, 5], [6, [7, [8, 9]]]]


def flatten(array: List[Any]):
    result = []
    for item in array:
        result.extend(item)
    return result

print(flatten(nested_arrays))  # [1, 2, 3, 4, 5, 6, [7, [8, 9]]]

The nested list is not flattened. So, the internal item needs to be checked whether or not it’s a list.

def recursive_flatten(array: List[Any]):
    result = []
    for item in array:
        if isinstance(item, list):
            result.extend(recursive_flatten(item))
        else:
            result.append(item)
    return result


print(recursive_flatten(nested_arrays))  # [1, 2, 3, 4, 5, 6, 7, 8, 9]

Check the following post if you search for another way to flatten a list.

Comments

Copied title and URL