programowanieBackend developer

Czym jest przekazywanie argumentów przez odniesienie i przez wartość w Pythonie? Jak Python realizuje ten mechanizm i dlaczego ważne jest ich rozróżnienie przy projektowaniu funkcji?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Zrozumienie, w jaki sposób Python przekazuje argumenty do funkcji, jest niezwykle ważne, aby zapobiec nieoczekiwanym zmianom danych i poprawnie zaprojektować kod.

Historia pytania

W tradycyjnych językach programowania, takich jak C lub Java, używa się przekazywania przez wartość (copy by value) lub przez odniesienie (copy by reference). Jednak w Pythonie obowiązuje inny model — call by object reference (czasami nazywane "call by sharing").

Problem

Wielu programistów błędnie sądzi, że Python zawsze przekazuje argumenty albo przez odniesienie, albo przez wartość. To nieuchronnie prowadzi do sytuacji, w których zmienne obiekty są niespodziewanie modyfikowane w kodzie wywołującym.

Rozwiązanie

W Pythonie wartości parametrów funkcji są odniesieniami do obiektów, które są przekazywane do funkcji. Oznacza to:

  • Jeżeli obiekt jest zmienny (mutable: lista, słownik, zestaw…) — można go modyfikować wewnątrz funkcji, a zmiany będą widoczne na zewnątrz.
  • Jeżeli obiekt jest niezmienny (immutable: int, str, tuple, frozenset), próba zmiany go wewnątrz funkcji spowoduje utworzenie nowego obiektu i nie wpłynie na obiekt zewnętrzny.

Przykład:

# lista - zmienny (mutable) def add_item(lst): lst.append(42) my_list = [1, 2, 3] add_item(my_list) print(my_list) # [1, 2, 3, 42] # int - niezmienny (immutable) def add_num(x): x = x + 1 num = 10 add_num(num) print(num) # 10

Kluczowe cechy:

  • Zmienne obiekty mogą być modyfikowane wewnątrz funkcji — te zmiany są widoczne na zewnątrz.
  • Niezmienne obiekty nie są dotykane przez funkcję — są tworzone tylko nowe obiekty.
  • Python nigdy nie kopiuje argumentów automatycznie, nawet zmienne struktury są zawsze przekazywane "przez odniesienie".

Pytania z pułapką.

Czy w Pythonie argumenty są zawsze przekazywane przez odniesienie?

Nie, w Pythonie przekazywane są odniesienia do obiektów, a to, jak zachowuje się obiekt, zależy od tego, czy jest zmienny czy nie. Niezmienny obiekt przy jakiejkolwiek zmianie utworzy nowy obiekt.

Czy można w funkcji przypisać nową wartość zmiennemu argumentowi, aby wpłynęło to na zewnętrzny obiekt?

Nie. Jeżeli wewnątrz funkcji przypiszesz nową wartość do parametru, zewnętrzny obiekt nie zmieni się — zmieniasz tylko lokalne odniesienie.

Przykład:

def reassign_list(lst): lst = [99, 100] my_list = [1, 2, 3] reassign_list(my_list) print(my_list) # [1, 2, 3]

Dlaczego funkcja, która przyjmuje listę domyślnie, może działać dziwnie przy kolejnych wywołaniach?

Ponieważ wartość domyślna jest tworzona tylko raz — przy definicji funkcji, a jeśli ją zmienisz (na przykład dodając element), zmieni się ona dla wszystkich kolejnych wywołań.

def add_element(x, cache=[]): cache.append(x) return cache print(add_element(1)) # [1] print(add_element(2)) # [1, 2]

Typowe błędy i antywzorce

  • Używanie zmiennych argumentów domyślnych (jak w ostatnim przykładzie).
  • Oczekiwanie, że funkcja nie zmodyfikuje przekazanej listy lub słownika, mimo że to ją zmienia.
  • Mieszanie efektów funkcji przy pracy z obiektami zmiennymi i niezmiennymi.

Przykład z życia

Negatywny przypadek

Programista przekazuje do funkcji listę i oczekuje, że jego oryginalna lista nie zmieni się, ale funkcja dodaje element.

Plusy:

  • Szybka praca, brak kopiowania danych.

Minusy:

  • Nieoczekiwane efekty uboczne, błędy w dużym kodzie, jeśli ktoś nie wie o modyfikacjach argumentu.

Pozytywny przypadek

Programista jawnie kopiuje listę wewnątrz funkcji, jeśli chce coś zwrócić, ale nie zmieniać oryginału:

def process_data(data): data = data.copy() # lub list(data) # bezpieczna praca z kopią data.append('raport') return data

Plusy:

  • Brak niepożądanych efektów ubocznych, oryginał chroniony.

Minusy:

  • Przy dużych obiektach — koszty pamięci/czasu na kopiowanie.