Что происходит, когда переменная передается по значению в Python?

Ответ

В Python все передается по присваиванию объекта (pass-by-object-reference), но для неизменяемых типов это ведет себя как передача по значению.

Для неизменяемых типов (int, float, str, tuple):

def modify_value(x):
    x = x + 10  # Создается новый объект
    print(f"Inside function: {x}")  # 20

num = 10
modify_value(num)
print(f"Outside function: {num}")  # 10 - оригинал не изменился

Для изменяемых типов (list, dict, set):

def modify_list(lst):
    lst.append(4)  # Изменяет оригинальный объект
    print(f"Inside function: {lst}")  # [1, 2, 3, 4]

my_list = [1, 2, 3]
modify_list(my_list)
print(f"Outside function: {my_list}")  # [1, 2, 3, 4] - изменился!

Ключевые моменты:

  1. Функция получает ссылку на тот же объект в памяти
  2. Для неизменяемых типов операция создает новый объект, не затрагивая оригинал
  3. Для изменяемых типов изменения видны снаружи функции

Как создать копию для изменяемых объектов:

import copy

def safe_modify(lst):
    lst_copy = lst.copy()  # или copy.deepcopy() для вложенных структур
    lst_copy.append(100)
    return lst_copy

Ответ 18+ 🔞

Давай разберем эту тему, а то она у многих мозги выносит, как будто они мартышлюшки на бананах. Сидишь такой, пишешь код, думаешь — ну вот же, передал переменную, а она нихуя не меняется. Или наоборот, меняется там, где не просил. Удивление пиздец.

Вот смотри, в Python всё передаётся по ссылке на объект. Но это не как в других языках, где ты прям ручками указатели крутишь. Тут проще: функция получает не копию значения, а ссылку на ту же самую штуку в памяти. Но дальше начинается магия, ёпта.

С неизменяемыми типами (int, float, str, tuple) — тут всё просто, как палка.
Ты передал число, внутри функции попытался его поменять — а него нихуя не меняется, потому что оно неизменяемое. Вместо этого создаётся новый объект, а старая ссылка на оригинал снаружи остаётся нетронутой. Вот смотри:

def modify_value(x):
    x = x + 10  # Тут создаётся новый объект, старый на месте
    print(f"Inside function: {x}")  # 20

num = 10
modify_value(num)
print(f"Outside function: {num}")  # 10 - оригинал как был, так и остался

Видишь? Внутри функции x стал 20, но снаружи num так и застрял на 10. Потому что числа — они как святые, их не перепишешь. Создали новое, старое на месте. Доверия ебать ноль к тому, что оно изменится.

А вот с изменяемыми типами (list, dict, set) — тут уже веселее, тут можно накосячить так, что мама не горюй.
Передал список, внутри функции в него что-то запихнул — и снаружи оно тоже изменилось! Потому что это тот же самый объект в памяти, просто у тебя две ссылки на него — одна внутри функции, другая снаружи.

def modify_list(lst):
    lst.append(4)  # А вот тут мы лезем прямо в оригинал, бля
    print(f"Inside function: {lst}")  # [1, 2, 3, 4]

my_list = [1, 2, 3]
modify_list(my_list)
print(f"Outside function: {my_list}")  # [1, 2, 3, 4] - охуеть, изменился!

Вот это и есть тот самый подводный камень, про который все орут. Кажется, что функция работает со своей копией, а она, сука, лезет в твои исходные данные и там всё перекраивает. Подозрение ебать чувствую, когда вижу такой код.

Так что запомни раз и навсегда:

  1. Функция получает ссылку на тот же объект. Не копию, а именно ссылку.
  2. Если тип неизменяемый (число, строка, кортеж) — любая операция с ним создаст новый объект, а старый останется в покое.
  3. Если тип изменяемый (список, словарь, множество) — любые изменения внутри функции ударят по оригиналу. Волнение ебать начинается, когда это понимаешь.

Как не облажаться?
Если не хочешь, чтобы функция портила твои исходные данные — делай копию. Не просто присваивай, а именно копируй объект.

import copy

def safe_modify(lst):
    lst_copy = lst.copy()  # Простая копия, если список одноуровневый
    # Или copy.deepcopy(), если там внутри ещё списки и словари сидят
    lst_copy.append(100)
    return lst_copy

Вот и вся наука. Главное — понимать, с чем работаешь. А то будешь как тот полупидор, который думает, что передал копию, а на деле хуй с горы получил. Удачи, не накосячь.