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ą w 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)
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.