Zrozumienie, w jaki sposób Python przekazuje argumenty do funkcji, jest niezwykle ważne, aby zapobiec nieoczekiwanym zmianom danych i poprawnie zaprojektować kod.
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").
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.
W Pythonie wartości parametrów funkcji są odniesieniami do obiektów, które są przekazywane do funkcji. Oznacza to:
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:
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]
Programista przekazuje do funkcji listę i oczekuje, że jego oryginalna lista nie zmieni się, ale funkcja dodaje element.
Plusy:
Minusy:
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:
Minusy: