Historia pytania:
Operatory * i ** do rozpakowywania pojawiły się w Pythonie dawno, ale ich zastosowanie rosło z każdą wersją (na przykład, od Pythona 3.5 dodano wsparcie dla łączenia kilku kolekcji za pomocą * i **). Te operatory sprawiają, że praca z kolekcjami jest bardziej elastyczna, w tym przy przekazywaniu argumentów i ich zbieraniu w funkcjach.
Problem:
Bez rozpakowywania podczas pracy z dynamicznymi sekwencjami, przekazywaniem parametrów z zmienną liczbą argumentów, pojawia się konieczność ręcznego pisania pętli i sprawdzania wymiarów kolekcji. Błędy łatwo się pojawiają przy niepoprawnym użyciu lub jeśli nie rozróżnia się przeznaczenia * i **.
Rozwiązanie:
Operator * jest przeznaczony do rozpakowywania sekwencji (list, tuple, set), a operator ** — do rozpakowywania słowników przy wywoływaniu funkcji lub łączeniu kilku słowników. Umożliwiają one eleganckie przekazywanie argumentów, łączenie kolekcji, łatwe przekształcanie dowolnych struktur w parametry funkcji.
Przykład kodu:
def foo(a, b, c): print(a, b, c) args = (1, 2, 3) foo(*args) # 1 2 3 params = {'a': 10, 'b': 20, 'c': 30} foo(**params) # 10 20 30 list1 = [1, 2] list2 = [3, 4] combined = [*list1, *list2] print(combined) # [1, 2, 3, 4]
Kluczowe cechy:
*sequence przekazuje elementy jako oddzielne argumenty pozycyjne, **dict jako argumenty nazwane.[*a, *b] i {**d1, **d2}.Czy można używać * i ** dla mieszanych kolekcji?
Przy wywołaniu funkcji * działa tylko z argumentami pozycyjnymi, a ** tylko z argumentami nazwanymi. Jeśli przekażesz nierozpakowany dict jako * lub sekwencję jako **, pojawi się błąd.
def foo(a, b): print(a, b) foo(*{'a': 1, 'b': 2}) # Wydrukuje: a b (klucze słownika, nie wartości!)
**Co się stanie, jeśli nazwy kluczy się pokrywają przy łączeniu dict przez {**d1, d2}?
W rezultacie będzie wartość z ostatniego słownika z takim kluczem.
d1 = {'x': 1, 'y': 2} d2 = {'y': 33, 'z': 44} merged = {**d1, **d2} print(merged) # {'x': 1, 'y': 33, 'z': 44}
Czy można używać * i ** wewnątrz wyrażeń listowych lub słownikowych?
Tak, jest to dozwolone od Pythona 3.5, na przykład:
lst = [1, 2, *range(3, 6)] # [1, 2, 3, 4, 5] dct = {**{'a': 1}, 'b': 2, **{'c': 3}}
Programista przyjmuje w wejściu funkcji dict i rozkłada go przy użyciu * zamiast **. Dochodzi do nieoczekiwanego zachowania: do funkcji trafiają nie wartości, a klucze.
Zalety:
Kod nie zawiesza się od razu, wydaje się „działający”.
Wady:
Ukryte błędy i niezgodność z oczekiwaną logiką.
Poprawne przekazywanie parametrów przez **kwargs, staranne łączenie słowników, użycie * przy dynamicznym łączeniu sekwencji.
Zalety:
Maksymalna elastyczność, zwięzłość kodu, łatwość refaktoryzacji.
Wady:
Przy dużej liczbie parametrów i kolekcji ważne jest, aby uważnie obserwować nazwy i kolejność, w przeciwnym razie mogą wystąpić błędy.