Короткий ответ: Python всегда передает по значению, но каждая переменная Python на самом деле является указателем на какой-либо объект, поэтому иногда это выглядит как передача по ссылке.
В Python каждый объект является изменяемым или неизменяемым. например, списки, словари, модули и фреймы данных Pandas являются изменяемыми, а целые числа, строки и кортежи неизменяемы. Изменяемые объекты могут быть изменены внутри (например, добавить элемент в список), но неизменяемые объекты - нет.
Как я сказал в начале, вы можете думать о каждой переменной Python как о указателе на объект. Когда вы передаете переменную в функцию, переменная (указатель) внутри функции всегда является копией переданной переменной (указателя). Поэтому, если вы присваиваете что-то новое внутренней переменной, все, что вы делаете, это изменяете локальная переменная, указывающая на другой объект. Это не изменяет (мутирует) исходный объект, на который указывает переменная, и не заставляет внешнюю переменную указывать на новый объект. На этом этапе внешняя переменная по-прежнему указывает на исходный объект, но внутренняя переменная указывает на новый объект.
Если вы хотите изменить исходный объект (возможно только с изменяемыми типами данных), вы должны сделать что-то, что изменит объект, не присваивая полностью новое значение локальной переменной. Поэтому letgo()
и letgo3()
оставляем внешний элемент неизменным, но letgo2()
изменяем его.
Как отметил @ursan, если letgo()
вместо этого использовать что-то подобное, то он изменит (мутирует) исходный объект, на который df
указывает, что изменит значение, видимое через глобальную a
переменную:
def letgo(df):
df.drop('b', axis=1, inplace=True)
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
letgo(a)
В некоторых случаях вы можете полностью выдолбить исходную переменную и заполнить ее новыми данными, фактически не выполняя прямого присвоения, например, это изменит исходный объект, на который v
указывает, что изменит данные, отображаемые при использовании v
позже:
def letgo3(x):
x[:] = np.array([[3,3],[3,3]])
v = np.empty((2, 2))
letgo3(v)
Обратите внимание, что я не назначаю что-либо напрямую x
; Я что-то присваиваю всему внутреннему диапазону x
.
Если вам абсолютно необходимо создать совершенно новый объект и сделать его видимым извне (что иногда бывает с пандами), у вас есть два варианта. Вариант "чистый" - это просто вернуть новый объект, например,
def letgo(df):
df = df.drop('b',axis=1)
return df
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
a = letgo(a)
Другой вариант - выйти за пределы вашей функции и напрямую изменить глобальную переменную. Это изменится, a
чтобы указать на новый объект, и любая функция, которая будет ссылаться a
позже, увидит этот новый объект:
def letgo():
global a
a = a.drop('b',axis=1)
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
letgo()
Прямое изменение глобальных переменных обычно является плохой идеей, потому что любому, кто читает ваш код, будет сложно понять, как a
были изменены. (Обычно я использую глобальные переменные для общих параметров, используемых многими функциями в скрипте, но я не позволяю им изменять эти глобальные переменные.)