Как в Python передаются аргументы в функции: по ссылке или по значению?

Ответ

В Python используется механизм передачи аргументов, известный как call by object reference (или call by sharing). Это означает, что в функцию передается копия ссылки на объект, а не сам объект или его полная копия.

Поведение зависит от типа данных (изменяемый или неизменяемый):

  1. Неизменяемые типы (immutable): int, str, tuple, frozenset. При попытке изменить такой объект внутри функции создается новый объект, и локальная переменная функции начинает ссылаться на него. Исходный объект вне функции остается без изменений.

    def change_immutable(s):
        # Создается новая строка, и 's' теперь ссылается на нее
        s = s + " world"
        print(f"Внутри функции: {s}") # Внутри функции: hello world
    
    my_string = "hello"
    change_immutable(my_string)
    print(f"Снаружи функции: {my_string}") # Снаружи функции: hello
  2. Изменяемые типы (mutable): list, dict, set. Поскольку функция получает ссылку на тот же самый объект, изменение его внутреннего состояния (например, добавление элемента в список) будет видно и за пределами функции.

    def change_mutable(lst):
        # Объект изменяется по ссылке
        lst.append(4)
        print(f"Внутри функции: {lst}") # Внутри функции: [1, 2, 3, 4]
    
    my_list = [1, 2, 3]
    change_mutable(my_list)
    print(f"Снаружи функции: {my_list}") # Снаружи функции: [1, 2, 3, 4]

Ключевой момент: Оператор присваивания (=) внутри функции всегда создает новую локальную ссылку и никогда не изменяет переменную вне ее области видимости.

Ответ 18+ 🔞

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

Вот смотри, в Python это называется call by object reference, или, по-нашему, «передача по ссылке на объект». По сути, в функцию летит не сам объект, а копия ссылки на него. Как будто ты дал другу дубликат ключа от своей квартиры, а не переехал к нему всей семьёй.

И тут вся соль — в типах данных, блядь. Они делятся на два лагеря:

  1. Неизменяемые (immutable): int, str, tuple, frozenset. Это как каменная скрижаль, высеченная в граните. Попробуй её изменить внутри функции — хуй там! Вместо этого создаётся новый объект, и твоя локальная переменная начинает тыкать пальцем уже в него. Оригинал снаружи остаётся нетронутым, как святой.

    def change_immutable(s):
        # Тут рождается новая строка, и 's' теперь смотрит на неё
        s = s + " world"
        print(f"Внутри функции: {s}") # Внутри функции: hello world
    
    my_string = "hello"
    change_immutable(my_string)
    print(f"Снаружи функции: {my_string}") # Снаружи функции: hello

    Видишь? Снаружи всё та же «hello». Функция порезвилась со своей копией, а твоя переменная даже не чихнула.

  2. Изменяемые (mutable): list, dict, set. А вот это уже трясина, болото! Объект один и тот же, и ссылка ведёт прямо к нему в логово. Начал внутри функции ковыряться — так он и снаружи весь изгаженный будет.

    def change_mutable(lst):
        # Объект меняется по той же самой ссылке, ёпта!
        lst.append(4)
        print(f"Внутри функции: {lst}") # Внутри функции: [1, 2, 3, 4]
    
    my_list = [1, 2, 3]
    change_mutable(my_list)
    print(f"Снаружи функции: {my_list}") # Снаружи функции: [1, 2, 3, 4]

    Вот тут уже овердохуища! Добавили четвёрку внутри, и она прилипла, как дерьмо к ботинку, и снаружи. Потому что список — он один, а ссылок на него — две.

Главный подвох, на котором все ебутся: оператор присваивания (=) внутри функции — это всегда создание новой локальной ссылки. Он никогда не идёт менять какую-то переменную снаружи, это не его ебаная работа. Хочешь изменить что-то снаружи — лезь прямо в кишки изменяемого объекта, если он, конечно, mutable. А иначе — в рот меня чих-пых, ничего не выйдет.