Pytanie

Czy w języku Python zmienne przekazywane są przez wartość, czy przez referencję ?

Dobra odpowiedz

Oczywiście przez referencję.

Krótkie przypomnienie

Przekazywanie przez wartość

Czym różni się przekazywanie przez wartość od przekazywania przez referencję ?  Jeśli przekazujemy zmienne przez wartość oznacza to, że wewnątrz funkcji operujemy na kopii naszej zmiennej. Innym słowem mamy gwarancję, że to co wydarzy się z naszą zmiennąfunkcji nie wpłynie na jej wartość na zewnątrz.

Przekazywanie przez referencję

W  tym przypadku wszytko co stanie się z naszą zmienną wewnątrz funkcji wpłynie na jej wartość. Zatem zarówno w funkcji jak i poza nią operujemy na rzeczywistej zmiennej. Oto przykład:

def przekazywanie_przez_ref(x):
    x.append('pong')
    print(x)


tekst = ['ping']
przekazywanie_przez_ref(tekst)
print(tekst)

Output:

['ping', 'pong']
['ping', 'pong']

Tym sposobem udowodniliśmy, że modyfikacja parametru wewnątrz funkcji spowodowała jej zmianę również poza nią. Zatem hura, mieliśmy rację to REFERENCJA.

Ale czy aby na pewno?

Rozważmy taki przykład

def przekazywanie_przez_ref(x):
    x = x + 1
    print(x)


tekst = 1
przekazywanie_przez_ref(tekst)
print(tekst)

Output:

2
1

Ojej. Zmiana parametru x wewnątrz funkcji nie spowodowało jej zmiany na zewnątrz. Może jednak przekazujemy zmienne przez wartość ? Spotkałem się nawet z odpowiedzią, że niektóre wartości przekazywane są przez wartość, a inne przez referencje. To nie prawda. W Pythonie zawsze i wszystkie zmienne przekazywane są przez referencję. Dobrze zatem co z naszym przykładem. Odpowiedz tkwi w słowie immutable.

immutable

Immutable oznacza niezmienne. Zatem mamy w Pythonie typy, których nie możemy modyfikować. Takim typem jest również typ int. Ok ale przecież udało nam się wykonać taką operację x = x + 1.. Jeśli zmienna x jest niezmienna to czemu udało nam się przypisać do niej nową wartość wewnątrz funkcji ? Odpowiedz jest prosta: to już nie jest nasza zmienna. W tej linii tworzona jest zupełnie nowa zmienna x o zasięgu funkcji. Zatem w momencie wyjścia z funkcji oryginalny x nie jest zmodyfikowany. Zaraz to udowodnimy za pomocą funkcji id(). Funkcja ta zwraca unikalne id obiektu dla jakiego wykonamy tą funkcje. Zatem jeśli dwie zmienne mają to samo id mamy gwarancję, że to ta sama zmienna.

def przekazywanie_przez_ref(x):
    print(f'zmienna x na początku funkcji {id(x)}')
    x = x + 1
    print(f'zmienna x na końcu funkcji {id(x)}')
    print(x) 

tekst = 1
print(f'id zmiennej przed wywolaniem funkcji {id(tekst)}')
przekazywanie_przez_ref(tekst)
print(f'id zmiennej po wywolaniu funkcji {id(tekst)}')
print(tekst)

Uruchom w edytorze

Output:

id zmiennej przed wywolaniem funkcji 10968800
zmienna x na początku funkcji 10968800
zmienna x na końcu funkcji 10968832
2
id zmiennej po wywolaniu funkcji 10968800
1

Jak widać wartość zmiennej tekst zarówno przed jak i po wywołaniu funkcji jest taka sama. Zupełnie inaczej wygląda sprawa ze zamienną x wewnątrz funkcji. Jak widzisz wartość id zmiennej x uległa zmianie. Jest to dowód na to, że wewnątrz funkcji powstała nowa zmienna x z lokalnym zakresem ważności.