Если я передаю фрейм данных функции и изменю его внутри функции, будет ли это передача по значению или по ссылке?
Я запускаю следующий код
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
def letgo(df):
df = df.drop('b',axis=1)
letgo(a)
значение a
не меняется после вызова функции. Означает ли это, что это передача по значению?
Я также пробовал следующее
xx = np.array([[1,2], [3,4]])
def letgo2(x):
x[1,1] = 100
def letgo3(x):
x = np.array([[3,3],[3,3]])
Оказывается, letgo2()
действительно меняется, xx
а letgo3()
не меняется. Почему это так?
Ответы:
Короткий ответ: 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) # will alter a
В некоторых случаях вы можете полностью выдолбить исходную переменную и заполнить ее новыми данными, фактически не выполняя прямого присвоения, например, это изменит исходный объект, на который
v
указывает, что изменит данные, отображаемые при использованииv
позже:def letgo3(x): x[:] = np.array([[3,3],[3,3]]) v = np.empty((2, 2)) letgo3(v) # will alter 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() # will alter a!
Прямое изменение глобальных переменных обычно является плохой идеей, потому что любому, кто читает ваш код, будет сложно понять, как
a
были изменены. (Обычно я использую глобальные переменные для общих параметров, используемых многими функциями в скрипте, но я не позволяю им изменять эти глобальные переменные.)источник
Чтобы добавить к ответу @Mike Graham, который указал на очень хорошее чтение:
В вашем случае важно помнить разницу между именами и значениями .
a
,df
,xx
,x
, Все имена , но они относятся к тем же или разным значениям в разных точках ваших примеров:В первом примере
letgo
выполняется повторная привязкаdf
к другому значению, потому чтоdf.drop
возвращается новое,DataFrame
если вы не установили аргументinplace = True
( см. Документ ). Это означает, что имяdf
(локальное дляletgo
функции), которое относилось к значениюa
, теперь относится к новому значению, здесь - кdf.drop
возвращаемому значению. Значение, о которомa
идет речь, все еще существует и не изменилось.Во втором примере,
letgo2
мутируетx
, без подмены его, из -за чегоxx
изменяютсяletgo2
. В отличие от предыдущего примера, здесь локальное имяx
всегда относится к значению, наxx
которое ссылается имя , и меняет это значение на месте , поэтому значение, наxx
которое ссылается, изменилось.В третьем примере
letgo3
выполняется повторная привязкаx
к новомуnp.array
. Это приводит к тому, что имяx
, локальноеletgo3
и ранее ссылающееся на значениеxx
, теперь будет ссылаться на другое значение, новоеnp.array
. Значение, о которомxx
идет речь, не изменилось.источник
Вопрос не в сравнении PBV и PBR. Эти имена вызывают путаницу только в таком языке, как Python; они были изобретены для языков, которые работают как C или как Fortran (как квинтэссенция языков PBV и PBR). Это правда, но не поучительно, что Python всегда передает значение. Вопрос здесь в том, мутирует ли само значение или вы получаете новое значение. Панды обычно ошибаются на стороне последнего.
http://nedbatchelder.com/text/names.html очень хорошо объясняет, что такое система имен Python.
источник
Python не передается ни по значению, ни по ссылке. Передается по назначению.
Вспомогательная ссылка, FAQ по Python: https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference
IOW:
Итак, если вы передадите список и измените его 0-е значение, это изменение будет видно как в вызываемом, так и в вызывающем. Но если вы переназначаете список новым списком, это изменение теряется. Но если вы отрезаете список и заменить , что с новым списком, что изменение проявляется в обоих вызываемая и вызывающей.
НАПРИМЕР:
def change_it(list_): # This change would be seen in the caller if we left it alone list_[0] = 28 # This change is also seen in the caller, and replaces the above # change list_[:] = [1, 2] # This change is not seen in the caller. # If this were pass by reference, this change too would be seen in # caller. list_ = [3, 4] thing = [10, 20] change_it(thing) # here, thing is [1, 2]
Если вы поклонник C, вы можете думать об этом как о передаче указателя по значению, а не как указатель на указатель на значение, а просто как указатель на значение.
HTH.
источник
Вот документ для падения:
Итак, создается новый фрейм данных. Оригинал не изменился.
Но что касается всех объектов в Python, фрейм данных передается в функцию по ссылке.
источник
df
внутри функции, разве это не означает, что указанное значение было изменено на новый объект?вам нужно сделать глобальную переменную «a» в начале функции, иначе это будет локальная переменная, которая не изменяет «a» в основном коде.
источник