The only difference I can think of between
[output1, output2] = some_func()
and
(output1, output2) = some_func()
is that the latter's memory footprint should be smaller (but I'm sure that this is an implementation detail) since tuples take less memory than lists with the same number of elements, mainly because tuples are immutable (so the interpreter should not need to worry about adding or removing [ie re-allocating memory] elements).
import sys
print(sys.getsizeof([1, 2]))
print(sys.getsizeof((1, 2)))
print(sys.getsizeof([1, 2, 3, 4]))
print(sys.getsizeof((1, 2, 3, 4)))
print(sys.getsizeof(list(range(1000))))
print(sys.getsizeof(tuple(range(1000))))
# 80
# 64
# 96
# 80
# 9112
# 8048
The generated bytecode is exactly the same for all 3 examples:
from dis import dis
def foo(): return 1, 2
def a():
output1, output2 = foo()
def b():
[output1, output2] = foo()
def c():
(output1, output2) = foo()
dis(a)
print('-----------------------------------------------------')
dis(b)
print('-----------------------------------------------------')
dis(c)
outputs
81 0 LOAD_GLOBAL 0 (foo)
2 CALL_FUNCTION 0
4 UNPACK_SEQUENCE 2
6 STORE_FAST 0 (output1)
8 STORE_FAST 1 (output2)
10 LOAD_CONST 0 (None)
12 RETURN_VALUE
-----------------------------------------------------
85 0 LOAD_GLOBAL 0 (foo)
2 CALL_FUNCTION 0
4 UNPACK_SEQUENCE 2
6 STORE_FAST 0 (output1)
8 STORE_FAST 1 (output2)
10 LOAD_CONST 0 (None)
12 RETURN_VALUE
-----------------------------------------------------
89 0 LOAD_GLOBAL 0 (foo)
2 CALL_FUNCTION 0
4 UNPACK_SEQUENCE 2
6 STORE_FAST 0 (output1)
8 STORE_FAST 1 (output2)
10 LOAD_CONST 0 (None)
12 RETURN_VALUE