Понимание того, каким образом Python передает аргументы в функции, крайне важно для предотвращения неожиданных изменений данных и для корректного проектирования кода.
В традиционных языках программирования, таких как C или Java, используется передача по значению (copy by value) или по ссылке (copy by reference). Однако в Python иная модель — call by object reference (иногда называют "call by sharing").
Многие разработчики ошибочно думают, что Python всегда или по ссылке, или по значению передает аргументы. Это неизбежно приводит к ситуациям, когда изменяемые объекты неожиданно модифицируются в вызывающем коде.
В Python значения параметров функции являются ссылками на объекты, которые передаются в функцию. Это означает:
Пример:
# list - изменяемый (mutable) def add_item(lst): lst.append(42) my_list = [1, 2, 3] add_item(my_list) print(my_list) # [1, 2, 3, 42] # int - неизменяемый (immutable) def add_num(x): x = x + 1 num = 10 add_num(num) print(num) # 10
Ключевые особенности:
Передаются ли в Python всегда аргументы по ссылке?
Нет, в Python передаются ссылки на объекты, и то, как ведет себя объект, зависит от того, изменяемый он или нет. Неизменяемый объект при любом изменении создаст новый объект.
Можно ли в функции переприсвоить изменяемый аргумент, и чтобы это повлияло на внешний объект?
Нет. Если вы внутри функции присваиваете параметру новое значение, внешний объект никак не изменится — вы лишь меняете локальную ссылку.
Пример:
def reassign_list(lst): lst = [99, 100] my_list = [1, 2, 3] reassign_list(my_list) print(my_list) # [1, 2, 3]
Почему функция, которая принимает list по умолчанию, может работать странно при повторных вызовах?
Потому что значение по умолчанию создается один раз — при определении функции, и если его изменить (например, добавить элемент), оно изменится для всех последующих вызовов.
def add_element(x, cache=[]): cache.append(x) return cache print(add_element(1)) # [1] print(add_element(2)) # [1, 2]
Программист передаёт в функцию список и ожидает, что его оригинальный список не изменится, но функция добавляет элемент.
Плюсы:
Минусы:
Программист явно копирует список внутри функции, если надо что-то вернуть, но не менять оригинал:
def process_data(data): data = data.copy() # или list(data) # безопасная работа с копией data.append('отчёт') return data
Плюсы:
Минусы: