programowanieProgramista Backend

Jak działa mechanizm pracy z argumentami pozycyjnymi i kluczowymi (positional and keyword arguments) w Pythonie? Jakie są niuanse dotyczące kolejności argumentów podczas definiowania funkcji i wywołania oraz do czego potrzebne są tylko argumenty pozycyjne i tylko nazwane?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

W Pythonie można deklarować parametry funkcji czterech rodzajów: pozycyjne, nazwane (keyword), tylko pozycyjne (positional-only) i tylko nazwane (keyword-only). Ich różnice wpływają na sposób przekazywania wartości podczas wywoływania funkcji.

Typy parametrów

  1. Pozycyjne (zwykle pierwsze): wartości przekazywane są w kolejności wskazanej.
  2. Nazwane: jawnie wskazywane przez nazwę.
  3. Tylko pozycyjne: deklarowane przed znakiem / (Python 3.8+), nie można wskazać ich po nazwie.
  4. Tylko nazwane: deklarowane po *, można je wskazać tylko po kluczu.
# Przykład wszystkich typów def func(a, b, /, c, *, d, e): print(a, b, c, d, e) func(1, 2, 3, d=4, e=5) # OK # func(a=1, b=2, 3, d=4, e=5) # Błąd: a, b - tylko pozycyjne

Niuanse

  • Kolejność parametrów: (only-positional) / (positional-or-keyword) * (only-keyword)
  • Przy błędnej kolejności argumentów — SyntaxError lub TypeError.
  • *args zbiera dodatkowe pozycyjne; **kwargs — nazwane.

Pytanie z haczykiem.

Czym różnią się deklaracje funkcji:

def f(a, b, c): ... def f(a, b, c=1): ... def f(a, b=1, c=2): ... def f(a=1, b=2, c=3): ...

i czy prawdą jest, że wszystkie te funkcje można wywoływać zarówno z argumentami pozycyjnymi, jak i kluczowymi?

Odpowiedź:

  • Dla funkcji def f(a, b, c): nie można wywołać wyłącznie z argumentami kluczowymi, ponieważ wszystkie można przekazać zarówno po pozycji, jak i po kluczu, ale wszystkie argumenty są obowiązkowe.
  • Można wskazywać parametry po nazwie, jeśli nie są określone jako tylko pozycyjne.
  • Jeśli funkcja jest zadeklarowana jako def f(a, b, /, c):, to a i b można przekazać tylko pozycyjnie.

Przykład:

def f(a, b, c=10): print(a, b, c) f(1, 2) # OK, c=10 domyślnie f(a=1, b=2, c=3) # OK # A oto: def f(a, b, /, c=10): ... f(1, 2) # OK f(a=1, b=2, c=3) # Błąd! a i b tylko pozycyjnie

Przykłady rzeczywistych błędów z powodu braku znajomości subtelności tematu


Historia

Programista zrealizował funkcję, która zgodnie z umową powinna przyjmować parametry wyłącznie po nazwie: logowanie z wieloma parametrami, część z nich opcjonalna. Jednak zapomniano zadeklarować * — i użytkownicy przypadkowo przekazywali parametry w różnej kolejności pozycyjnej, co prowadziło do niewłaściwego logowania i błędów, które były trudne do zlokalizowania.


Historia

W projekcie z REST API z powodu niejawnej umowy o kolejności argumentów (używano *args i nie ograniczano parametrów po nazwie) przestały działać zapytania po wprowadzeniu nowej wersji klienta — ponieważ args się przesunęły. Naprawiono to poprzez wprowadzenie jawnych * i wskazanie tylko nazwanych argumentów.


Historia

W dużym projekcie korporacyjnym funkcję rozszerzono o nowe parametry z wartościami domyślnymi, ale z powodu błędnego wywołania z wartościami pozycyjnymi stary kod zaczął podstawiać wartości do innych parametrów, co spowodowało nieprawidłowe przetwarzanie danych. Okazało się, że parametry powinny być tylko nazwane.