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.

Sponsored links

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.

Sponsored links

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