Python *args i **kwargs wyjaśnione: Elastyczne funkcje z przejrzystymi przykładami
Te gwiazdy w sygnaturach funkcji są mniej tajemnicze, niż na to wyglądają. Dowiedz się, jak *args i **kwargs zbierają argumenty, jak te same gwiazdy je rozpakowują i jak projektować sygnatury funkcji, które trudno będzie nadużyć.
*argszbiera dodatkowe argumenty pozycyjne w krotce.**kwargszbiera dodatkowe argumenty słów kluczowych w słowniku.- Nazwy są jedynie konwencją, ponieważ znaczenie mają
*i**. - Te same gwiazdki rozpakowują listę lub słownik podczas wywoływania funkcji.
Wcześniej czy później przeczytasz funkcję taką jak def handler(*args, **kwargs) i będziesz się zastanawiać, co akceptuje. Python *args i **kwargs pozwalają funkcji przyjmować elastyczną liczbę argumentów. Obsługują dekoratory, opakowania i wiele interfejsów API bibliotek.
W tym przewodniku opisano, jak działają one w obu kierunkach, a także nowsze sposoby, w jakie Python pozwala kontrolować sposób wywoływania funkcji. Każdy przykład uruchomiliśmy w Pythonie 3.13 i pokazane dane wyjściowe pochodzą z tego uruchomienia.
Co robi *args
Parametr zapisany jedną gwiazdką zbiera dodatkowe argumenty pozycyjne w krotce:
def total(*args):
print(type(args).__name__, args)
return sum(args)
print(total(1, 2, 3))
# tuple (1, 2, 3)
# 6
print(total())
# tuple ()
# 0
Bez argumentów otrzymujesz po prostu pustą krotkę, więc funkcja nadal działa. Nazwa args jest konwencją. *numbers zachowywałby się tak samo, a bardziej opisowa nazwa jest często lepsza.
Co robi **kwargs
Dwie gwiazdki gromadzą dodatkowe argumenty słów kluczowych w słowniku:
def describe(**kwargs):
print(type(kwargs).__name__, kwargs)
describe(name="Asha", city="Pune")
# dict {'name': 'Asha', 'city': 'Pune'}
Ponieważ jest to normalny słownik, możesz przeglądać go w pętli, szukać kluczy za pomocą get() i przekazywać go dalej. Klucze zachowują kolejność, w jakiej zostały przekazane.
Kolejność parametrów
Kiedy mieszasz rodzaje parametrów, Python wymaga określonej kolejności:
- Regularne parametry pozycyjne
*argumenty- Parametry dotyczące tylko słów kluczowych
**kwargs
def order(item, qty=1, *extras, priority=False, **notes):
print(item, qty, extras, priority, notes)
order("pen")
# pen 1 () False {}
order("pen", 2, "gift wrap", "card", priority=True, note="leave at door")
# pen 2 ('gift wrap', 'card') True {'note': 'leave at door'}
Uwaga priority. Ponieważ pojawia się po *extras, można go ustawić tylko według nazwy, więc przypadkowy argument pozycyjny nigdy nie mógłby ustawić go przez przypadek. Wcześniejsze umieszczenie **kwargs jest niedozwolone, a nasz test zwrócił SyntaxError: „argumenty nie mogą następować po argumencie słowa kluczowego var”.
Rozpakowywanie podczas dzwonienia
Te same gwiazdy działają odwrotnie w miejscu połączenia. Jedna gwiazdka dzieli listę lub krotkę na argumenty pozycyjne, a dwie gwiazdki dzielą słownik na argumenty oparte na słowach kluczowych:
nums = [4, 5, 6]
total(*nums) # same as total(4, 5, 6) -> 15
settings = {"name": "Ben", "city": "Delhi"}
describe(**settings) # same as describe(name="Ben", city="Delhi")
Rozpakowywanie działa z dowolną funkcją, nie tylko z tymi, które korzystają z *args i **kwargs. Jest to przydatne, gdy konfiguracja pojawia się w postaci słownika:
def connect(host, port, timeout=5):
return f"{host}:{port} (timeout {timeout})"
connect(**{"host": "db.local", "port": 5432})
# 'db.local:5432 (timeout 5)'
connect(**{"host": "db.local", "port": 5432, "ssl": True})
# TypeError: connect() got an unexpected keyword argument 'ssl'
Ten błąd jest przydatny. Funkcja bez **kwargs odrzuca nieznane opcje, więc literówka w nazwie ustawienia kończy się głośnym niepowodzeniem, zamiast być dyskretnie ignorowana.
Argumenty zawierające wyłącznie słowa kluczowe
Pusta * w podpisie, bez nazwy, oznacza, że „wszystko po tym musi być przekazane za pomocą słowa kluczowego”:
def make_user(name, *, admin=False):
return name, admin
make_user("Asha", admin=True)
# ('Asha', True)
make_user("Asha", True)
# TypeError: make_user() takes 1 positional argument but 2 were given
To świetny sposób na ochronę flag boolowskich. Wywołanie takie jak make_user("Asha", True) zmusza czytelników do odgadnięcia, co oznacza True. Wymaganie admin=True sprawia, że każde wywołanie jest oczywiste.
Argumenty wyłącznie pozycyjne
Od wersji Python 3.8 / oznacza parametry, które mogą być przekazywane tylko przez pozycję:
def area(width, height, /):
return width * height
area(3, 4) # 12
area(width=3, height=4)
# TypeError: area() got some positional-only arguments
# passed as keyword arguments: 'width, height'
Dzięki temu biblioteka może później zmienić nazwy tych parametrów bez łamania niczyjego kodu, ponieważ nikt nie może polegać na nazwach. Kilka wbudowanych funkcji działa w ten sposób. Na przykład len(obj=[1]) nie działa.
Wskazówka: Pełny wzór to def f(pos_only, /, normal, *, kw_only). Rzadko potrzebujesz wszystkich trzech, ale wiedza, co oznaczają / i *, pomaga w czytaniu dokumentacji i podpisów bibliotek.
Dekoratory i opakowania
Jest to główny powód, aby używać obu razem. Opakowanie często musi zaakceptować dowolne argumenty oryginalnej funkcji i przekazać je bez zmian:
import functools, time
def timed(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
print(f"{func.__name__} took {time.perf_counter() - start:.4f}s")
return result
return wrapper
@timed
def slow_add(a, b, delay=0.01):
time.sleep(delay)
return a + b
print(slow_add(2, 3, delay=0.02))
# slow_add took 0.0205s
# 5
Opakowanie w ogóle nie musi znać parametrów slow_add, więc działa dla dowolnej funkcji. Dane dotyczące czasu będą się nieznacznie różnić. Nasz przewodnik po dekoratorach Pythona wyjaśnia functools.wraps i inne szczegóły.
Opcje łączenia
** działa również w dosłownym słowniku, co daje fajny sposób na zastosowanie nadpisań nad wartościami domyślnymi:
defaults = {"theme": "light", "lang": "en"}
def build(**overrides):
return {**defaults, **overrides}
build(theme="dark")
# {'theme': 'dark', 'lang': 'en'}
Późniejsze klucze wygrywają, więc nadpisanie zastępuje ustawienie domyślne. Podklasy również używają tego wzorca, jak w super().__init__(*args, **kwargs), aby przekazywać dodatkowe argumenty do klasy nadrzędnej. Zobacz Klasy i obiekty Pythona, aby uzyskać więcej informacji na temat super().
Ściągawka
| Składnia | Gdzie | Znaczenie |
|---|---|---|
def f(*args) |
Definicja | Zbierz dodatkowe argumenty pozycyjne w krotce |
zdecydowanie f(**kwargs) |
Definicja | Zbierz dodatkowe argumenty słów kluczowych w dyktacie |
zdecydowanie f(a, *, b) |
Definicja | b dotyczy wyłącznie słów kluczowych |
zdecydowanie f(a, /, b) |
Definicja | a ma wyłącznie charakter pozycyjny |
f(*items) |
Zadzwoń | Rozłóż sekwencję na argumenty pozycyjne |
f(**options) |
Zadzwoń | Rozłóż dyktando na argumenty słów kluczowych |
{**a, **b} |
Dosłowne | Połącz słowniki |
[*a, *b] |
Dosłowne | Połącz iteracje w listę |
Kiedy ich unikać
Elastyczność ma swoją cenę. Funkcja zdefiniowana jako def create(**kwargs) nie mówi czytelnikom, które opcje istnieją, a redaktorzy nie mogą ich automatycznie uzupełniać. Literówki mogą po cichu przejść przez kilka warstw, zanim cokolwiek zawiedzie.
Preferuj więc jawne parametry, jeśli wiesz, czego potrzebuje funkcja. Użyj *args i **kwargs, jeśli naprawdę potrzebujesz elastyczności: opakowania, dekoratory, przekazywanie argumentów do innej funkcji lub prawdziwe dane wejściowe o zmiennej długości, takie jak total(*numbers). Jeśli funkcja wymaga wielu nazwanych opcji, dataclass może być również jaśniejsze.
Typowe błędy
Używanie zmiennej domyślnej
Nie chodzi tu o gwiazdy, ale często pojawia się w ich pobliżu. Wartość domyślna tworzona jest jednorazowo podczas definiowania funkcji, a następnie udostępniana pomiędzy wywołaniami:
def bad(items=[]):
items.append(1)
return items
print(bad(), bad())
# [1, 1] [1, 1]
Obydwa wywołania zwróciły tę samą listę, która zawierała już dwie pozycje. Użyj items=None i utwórz nową listę wewnątrz funkcji.
Inne błędy
- Zapominam rozpakować. Dzwoniąc do
total(nums)przekazujemy jedną listę, a nie trzy numery. Napisztotal(*nums). - Połykanie literówek. Funkcja, która akceptuje
**kwargs, ale ignoruje nieznane klucze, ukrywa błędnie napisane opcje. Zweryfikuj je lub unikaj**kwargs. - Dwukrotne przekazanie tego samego argumentu.
f(1, **{"a": 2}), gdzieajest pierwszym parametrem, powoduje błąd „wiele wartości”. - Brak przekazywania w opakowaniu. Wywołanie
func(args, kwargs)przekazuje krotkę i dyktando. Poprawna forma tofunc(*args, **kwargs).
Wpisz wskazówki dla *args i **kwargs
Wskazówki dotyczące typów *args i **kwargs opisują każdą pojedynczą wartość, a nie całą kolekcję. To zaskakuje wiele osób:
def total(*prices: float) -> float:
return sum(prices) # prices is tuple[float, ...]
def tag(**attrs: str) -> str:
return " ".join(f'{k}="{v}"' for k, v in attrs.items()) # dict[str, str]
total(1.5, 2.5) # 4.0
tag(id="x", role="button") # id="x" role="button"
Tutaj prices jest traktowane jako krotka liczb zmiennoprzecinkowych, a attrs jako słownik ciągów. Gdy argumenty słów kluczowych mają różne typy, w Pythonie 3.12 dodano precyzyjną opcję: a TypedDict w połączeniu z Unpack.
from typing import TypedDict, Unpack
class Options(TypedDict, total=False):
timeout: float
retries: int
def fetch(url: str, **opts: Unpack[Options]) -> None: ...
Narzędzie do sprawdzania typu, takie jak mypy lub Pyright, może następnie odrzucić fetch(url, retries="3") lub błędnie napisaną nazwę opcji, czego zwykły **kwargs nigdy nie jest w stanie zrobić. Zachowanie środowiska wykonawczego nie zmienia się jednak. Kiedy uruchomiliśmy fetch("u", retries="3") w Pythonie 3.13, wykonano go bez zarzutu, ponieważ sam Python nie wymusza wskazówek dotyczących typów.
Jeśli dokumentujesz długą listę akceptowanych kluczy dla **kwargs, rozważ zastąpienie ich wyraźnymi parametrami zawierającymi tylko słowa kluczowe. Redaktorzy mogą następnie automatycznie je uzupełniać, a błędy są natychmiast wychwytywane.
Podsumowanie
Pamiętaj o dwóch kierunkach. Z definicji gwiazdy zbierają. Podczas rozmowy rozpakowują się. Dodaj argumenty składające się wyłącznie ze słów kluczowych, aby wywołania były jaśniejsze, i zarezerwuj **kwargs dla kodu, który rzeczywiście przekazuje argumenty. Aby poćwiczyć, napisz mały dekorator, który będzie rejestrował argumenty każdego wywołania i użyj go w kilku różnych funkcjach.
Najczęstsze pytania
Jaka jest różnica między *args i **kwargs?
*args zbiera dodatkowe argumenty pozycyjne w krotce. **kwargs zbiera dodatkowe argumenty słów kluczowych w słowniku. Funkcja może używać obu razem.
Czy muszę nazywać je args i kwargs?
Nie. Gwiazdy wykonują swoją pracę, więc *wartości i **opcje są równie ważne. args i kwargs to po prostu powszechnie uznawana konwencja.
W jakiej kolejności powinny znajdować się *args i **kwargs?
Najpierw parametry zwykłe, potem *args, następnie parametry zawierające tylko słowa kluczowe, a na końcu **kwargs. Każde inne zamówienie powoduje błąd SyntaxError.
Co oznacza pojedynczy * w definicji funkcji?
Sam znak * oznacza, że każdy parametr po nim musi zostać przekazany przez słowo kluczowe. Dzięki temu wywołania są wyraźniejsze, zwłaszcza w przypadku flag boolowskich.
Co oznacza/oznacza w funkcji Pythona?
Parametry przed / mają charakter wyłącznie pozycyjny, więc osoby wywołujące nie mogą przekazywać ich według nazwy. Ta funkcja została dodana w Pythonie 3.8.
Ucz się dalej
Potrzebujesz pomocy przy testowaniu, WordPressie lub SEO?
Pomagamy zespołom dostarczać lepsze oprogramowanie i rozwijać ich witryny. Powiedz nam, nad czym pracujesz, a my zaproponujemy jasny następny krok.
Więcej poradników na testowanie, sztuczna inteligencja i nowoczesny rozwój
Podobał Ci się ten artykuł? Wybierz temat i przejdź bezpośrednio do naszych najnowszych tutoriali.
Wyjaśnienie halucynacji AI: dlaczego modele językowe tworzą rzeczy i jak je zmniejszyć
Wyjaśniono halucynacje AI: czym są, dlaczego powstają w modelach językowych, typowe typy, jak je zredukować za pomocą uziemień i podpowiedzi oraz pr…
Przeczytaj artykuł →DevOpsWyjaśnienie zmiennych środowiskowych: pliki .env, sekrety i konfiguracja wykonana prawidłowo
Zmienne środowiskowe wyjaśnione na przykładach Pythona, które przeprowadziliśmy: ich odczytywanie i ustawianie, dlaczego każda wartość jest ciągiem …
Przeczytaj artykuł →Tworzenie oprogramowaniaWyjaśnienie ograniczenia szybkości interfejsu API: algorytmy, nagłówki i obsługa błędów 429
Ograniczanie szybkości API wyjaśnione za pomocą przeprowadzonej przez nas symulacji: zasobnik tokenów, stałe okno, przesuwane okno, 429 odpowiedzi, …
Przeczytaj artykuł →Automatyzacja testówWyśmiewanie w Pythonie: unittest.mock, patch i błędy, które sprawiają, że testy kłamią
Wyśmiewanie w Pythonie za pomocą unittest.mock, przetestowane: Mock i zwracana wartość, efekt uboczny, łatanie tam, gdzie używana jest nazwa, specyf…
Przeczytaj artykuł →Testowanie oprogramowaniaWyjaśnienie zasięgu kodu: pokrycie linii i oddziału oraz co naprawdę oznacza 100%
Wyjaśnienie pokrycia kodu za pomocą prawdziwego pliku cover.py: pokrycie linii, gałęzi i warunków, przeczytanie raportu, dlaczego 100% pokrycia może…
Przeczytaj artykuł →Bazy danych i SQLDELETE vs TRUNCATE vs DROP w SQL: różnice, wycofywanie i kiedy używać każdego z nich
DELETE vs TRUNCATE vs DROP wyjaśnione za pomocą przetestowanego SQL: co każdy usuwa, wycofywanie, wyzwalacze, resetowanie tożsamości, prędkość i czy…
Przeczytaj artykuł →Bazy danych i SQLWyjaśnienie widoków SQL: tworzenie, używanie i unikanie pułapek wraz z przykładami
Widoki SQL wyjaśnione za pomocą zapytań, które uruchomiliśmy: tworzenie widoku, wyniki na żywo, ukrywanie kolumn, wydajność, widoki z możliwością ak…
Przeczytaj artykuł →Tworzenie oprogramowaniaInterfejs TypeScript a typ: rzeczywiste różnice, testowane za pomocą kompilatora
Interfejs TypeScript a typ, sprawdzony z prawdziwym kompilatorem: co oba mogą zrobić, co tylko jeden, łączenie deklaracji, rozszerzenia a przecięcia…
Przeczytaj artykuł →Tworzenie oprogramowaniaPython *args i **kwargs wyjaśnione: elastyczne funkcje z przejrzystymi przykładami
Python *args i **kwargs wyjaśnione za pomocą uruchomionego przez nas kodu: pakowanie i rozpakowywanie, kolejność parametrów, argumenty zawierające t…
Przeczytaj artykuł →Tworzenie oprogramowaniaFunkcje Lambda w Pythonie: składnia, rzeczywiste zastosowania i kiedy ich nie używać
Funkcje lambda w Pythonie wyjaśnione za pomocą uruchomionego przez nas kodu: składnia, sortowanie za pomocą klucza, mapa i filtr, domknięcia, pułapk…
Przeczytaj artykuł →Sztuczna inteligencjaKim są agenci AI? Jak one działają, dzięki prostej pętli agenta, którą możesz uruchomić
Kim są agenci AI? Prosty przewodnik po pętli agenta, narzędziach, pamięci i poręczach, z małą pętlą w Pythonie, którą uruchomiliśmy, prawdziwymi zag…
Przeczytaj artykuł →DevOpsMonitorowanie a obserwowalność: prawdziwa różnica z logami, wskaźnikami i śladami
Wyjaśnienie monitorowania a obserwowalności: znane i nieznane awarie, dzienniki, metryki i ślady, opis incydentu przy kasie, złote sygnały, OpenTele…
Przeczytaj artykuł →DevOpsWoluminy platformy Docker a mocowania powiązań: utrwalanie danych kontenera we właściwy sposób
Wyjaśniono woluminy Dockera a montowanie powiązań: dlaczego dane kontenera znikają, nazwane woluminy, montowanie powiązań, tmpfs, przykłady tworzeni…
Przeczytaj artykuł →Automatyzacja testówTDD vs BDD: kluczowe różnice, jedna funkcja zbudowana w obie strony i kiedy używać każdej z nich
TDD vs BDD wyjaśnione za pomocą jednej funkcji rabatowej zbudowanej w obie strony: refaktoryzacja czerwono-zielona z uruchomionym przez nas kodem, s…
Przeczytaj artykuł →Tworzenie oprogramowaniaGit merge vs rebase: różnice, przykłady i kiedy czego używać
Git merge vs rebase wyjaśnione na prawdziwym wyniku logu, z tabelą porównawczą, złotą zasadą rebase i bezpiecznym procesem pracy zespołu.
Przeczytaj artykuł →Tworzenie oprogramowaniaPython list comprehension: składnia, przykłady i kiedy jej unikać
Python list comprehension wyjaśnione: składnia, filtrowanie, if-else, zagnieżdżone pętle, zmierzone czasy i sytuacje, w których zwykła pętla for jes…
Przeczytaj artykuł →Bazy danych i SQLSQL JOIN – wyjaśnienie z przykładami (Inner, Left, Right, Full)
Zrozum złączenia SQL krok po kroku na przykładowych tabelach i zapytaniach: INNER, LEFT, RIGHT, FULL, CROSS i SELF, plus typowe błędy i wskazówki wy…
Przeczytaj artykuł →