python pandas dataframe, является ли это передачей по значению или передачей по ссылке

Если я передаю фрейм данных в функцию и изменяю ее внутри функции, то она передается по значению или передается по ссылке?

Я запускаю следующий код

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() не делает. Почему так?

Ответ 1

Короткий ответ заключается в том, что Python всегда выполняет посылку, но каждая переменная Python на самом деле является указателем на какой-либо объект, поэтому иногда он выглядит как pass-by-reference.

В Python каждый объект либо изменен, либо не изменен. например, списки, dicts, модули и кадры данных Pandas изменяемы, а ints, строки и кортежи не изменяются. Изменчивые объекты могут быть изменены внутренне (например, добавить элемент в список), но не изменяемые объекты не могут.

Как я сказал в начале, вы можете думать о каждой переменной 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 (Обычно я использую глобальные переменные для общих параметров, используемых многими функциями в скрипте, но я не позволяю им изменять эти глобальные переменные.)

Ответ 2

Вопрос не в PBV и PBR. Эти имена только вызывают путаницу на языке Python; они были изобретены для языков, которые работают как C или как Fortran (как типичные языки PBV и PBR). Это правда, но не поучительно, что Python всегда проходит по значению. Вопрос в том, является ли само значение изменено или вы получаете новое значение. Панды обычно ошибаются на стороне последнего.

http://nedbatchelder.com/text/names.html очень хорошо объясняет, что такое система имен Python.

Ответ 3

Чтобы добавить к ответу @Mike Graham, который указал на очень хорошее чтение:

В вашем случае важно помнить о различии между именами и значениями. a, df, xx, x, - все имена, но они относятся к одинаковым или различным значениям в разных точках ваших примеров:

  • В первом примере letgo переустанавливает df на другое значение, потому что df.drop возвращает новый DataFrame если вы не установите аргумент inplace = True (см. Doc). Это означает, что имя df (локальное для функции letgo), которое letgo на значение a, теперь относится к новому значению, здесь возвращаемое значение df.drop. Значение a относится к прежнему существованию и не изменилось.

  • Во втором примере letgo2 мутирует x, не перебирая его, поэтому xx модифицируется letgo2. В отличие от предыдущего примера, здесь локальное имя x всегда ссылается на значение, к которому ссылается имя xx, и изменяет это значение на месте, поэтому значение xx имеет значение.

  • В третьем примере letgo3 переставляет x в новый np.array. Это вызывает имя x, local to letgo3 и ранее ссылающееся на значение xx, теперь ссылающееся на другое значение - новый np.array. Значение xx имеет значение, которое не изменилось.

Ответ 4

Вот документ для капли:

Возвращает новый объект с метками на запрошенной оси.

Таким образом создается новый dataframe. Оригинал не изменился.

Но что касается всех объектов в python, кадр данных передается функции по ссылке.

Ответ 5

вам нужно сделать "a" глобальным в начале функции, иначе это локальная переменная и не изменит "a" в главном коде.

Ответ 6

Python не проходит ни по значению, ни по ссылке. Это пропуск по заданию.

Поддерживая ссылку, часто задаваемые вопросы по Python: https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference

IOW:

  1. Если вы передадите неизменяемое значение, изменения в нем не изменят его значение в вызывающем абоненте - потому что вы переписываете имя на новый объект.
  2. Если вы передаете изменяемое значение, изменения, внесенные в вызываемую функцию, также изменяют значение в вызывающем объекте, если вы не пересобираете это имя на новый объект. Если вы переназначите переменную, создав новый объект, это изменение и последующие изменения имени не будут видны в вызывающем.

Поэтому, если вы передадите список и измените его 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, вы можете думать об этом как о передаче указателя по значению - не указатель на указатель на значение, а только указатель на значение.

НТН.