Ответ
В 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] - изменился!
Ключевые моменты:
- Функция получает ссылку на тот же объект в памяти
- Для неизменяемых типов операция создает новый объект, не затрагивая оригинал
- Для изменяемых типов изменения видны снаружи функции
Как создать копию для изменяемых объектов:
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] - охуеть, изменился!
Вот это и есть тот самый подводный камень, про который все орут. Кажется, что функция работает со своей копией, а она, сука, лезет в твои исходные данные и там всё перекраивает. Подозрение ебать чувствую, когда вижу такой код.
Так что запомни раз и навсегда:
- Функция получает ссылку на тот же объект. Не копию, а именно ссылку.
- Если тип неизменяемый (число, строка, кортеж) — любая операция с ним создаст новый объект, а старый останется в покое.
- Если тип изменяемый (список, словарь, множество) — любые изменения внутри функции ударят по оригиналу. Волнение ебать начинается, когда это понимаешь.
Как не облажаться?
Если не хочешь, чтобы функция портила твои исходные данные — делай копию. Не просто присваивай, а именно копируй объект.
import copy
def safe_modify(lst):
lst_copy = lst.copy() # Простая копия, если список одноуровневый
# Или copy.deepcopy(), если там внутри ещё списки и словари сидят
lst_copy.append(100)
return lst_copy
Вот и вся наука. Главное — понимать, с чем работаешь. А то будешь как тот полупидор, который думает, что передал копию, а на деле хуй с горы получил. Удачи, не накосячь.