У меня есть функциональный test
который принимает DataFrame и добавляет к нему данные. Я хочу, чтобы глобальная переменная, помещенная в функцию, была изменена. У меня есть сценарий ниже:
import pandas as pd
global dff
def test(df):
df = df.append({'asdf':1, 'sdf':2}, ignore_index=True)
return(df)
dff = pd.DataFrame()
test(dff)
После этого dff
остается пустым; он не редактировался. Однако, если вы это сделаете:
import pandas as pd
def test(df):
df['asdf'] = [1,2,3]
return(df)
dff = pd.DataFrame()
test(dff)
dff
будет иметь [1,2,3]
под столбцом 'asfd'
. Обратите внимание, что мне даже не пришлось объявлять переменную global
.
Почему это происходит?
Я действительно хотел бы знать, потому что второй, я думаю, я понимаю переменные рабочие пространства, я доказал свою несостоятельность, и мне становится надоело постоянно работать в этой BS *
Я знаю, что решение проблемы:
import pandas as pd
def test(df):
df = df.append({'asdf':1, 'sdf':2}, ignore_index=True)
return(df)
dff = pd.DataFrame()
dff = test(dff)
но я просто пытаюсь понять, почему первоначальный метод не работает, особенно в свете второго сценария, который я показал.
* Очевидно, что это не полная BS, но я не могу понять это после 3 лет случайного программирования
Я нашел очень приятный разговор на PyCon 2015, который объясняет, что я пытаюсь объяснить ниже, но с диаграммами, которые делают его значительно яснее. Я оставлю объяснение ниже, чтобы объяснить, как работают оригинальные 3 скрипта, но я бы предложил посмотреть видео:
Ned Batchelder - Факты и мифы о названиях и ценностях Python - PyCon 2015
Итак, я думаю, что я понял, что происходит в двух сценариях выше. Я попробую разбить его. Не стесняйтесь исправить меня, если потребуется.
Несколько правил:
Переменные - это имена ссылок/указателей на базовый объект, который фактически хранит данные. Например, уличные адреса. Улица не является домом; он просто указывает на дом. Таким образом, адрес (101 Streetway Rd.) Является указателем. В GPS вы можете обозначить его как "Главная". Слово "дом" было бы самой переменной.
Функции работают с объектами, а не с переменными или указателями. Когда вы передаете переменную функции, вы фактически передаете объект, а не переменную или указатель. Продолжая пример дома, если вы хотите добавить колоду в дом, вы хотите, чтобы подрядчики настила работали над домом, а не с метафизическим адресом.
Команда return
в функции возвращает указатель на объект. Таким образом, это будет адрес дома, а не дом или имя, которое вы могли бы назвать своим домом.
=
это функция, означающая "point to this object". Переменная перед =
выход, переменная справа - это вход. Это был бы акт названия дома. Итак, Home = 101 Streetway Rd.
превращает переменную Home
point в дом на 101 Streetway Rd. Скажем, вы переехали в дом ваших соседей, который является 102 Streetway Rd. Это может быть сделано Home = Neighbor House
. Теперь Home
теперь называется указателем 102 Streetway Rd.
Здесь, я буду использовать --->
для обозначения "указывает на",
Прежде чем мы перейдем к сценариям, начнем с того, что мы хотим. Мы хотим, чтобы объект objdff
указывал на varia
(без global dff
поскольку это ничего не делает)
import pandas as pd def test(df): df = df.append({'asdf':1, 'sdf':2}, ignore_index=True) return(df) dff = pd.DataFrame() test(dff)
Итак, пройдите через функцию. Ничего интересного не происходит, пока мы не доберемся до:
dff = pd.DataFrame()
Здесь мы имеем varible dff
быть назначены на объект, созданный pd.DataFrame
, который является пустым dataframe. Мы будем называть этот объект objdff
. Итак, в конце этой строки мы имеем dff ---> objdff
.
Следующая строка: test(dff)
Функции работы на объектах, поэтому мы говорим, что мы собираемся запустить функцию test
на объекте, dff
очков, что objdff
. Это приводит нас к самой функции.
def test(df):
Здесь мы имеем то, что является по существу =
функция. Объект, переданный тестовой функции objdff
, указывает на функциональную переменную df
. Итак, теперь df --->objdff
и dff---> objdff
Перемещение на следующую строку: df = df.append(...)
Начнем с df.append(...)
. .append(...)
передается на objdff
. Это заставляет объект objdff
запускать функцию, называемую "append". Как отметил @Jai, метод .append(...)
использует команду return
для вывода абсолютно нового DataFrame, к которому добавлены данные. Мы будем называть новый объект objdff_apnd
.
Теперь мы можем перейти на часть df =...
Теперь у нас есть df = objdff_apnd
. Сейчас это довольно просто. Переменная df
теперь указывает на объект objdff_apnd
.
В конце этой строки мы имеем df ---> objdff_apnd
и df ---> objdff_apnd
dff ---> objdff
. Здесь и лежит проблема. Объект, который мы хотим (objdff_apnd
), не указывает на dff
.
Поэтому в конце переменная dff
по-прежнему указывает на objdff
, а не objdff_apnd
. Это приводит нас к сценарию 3 (см. Ниже).
import pandas as pd def test(df): df['asdf'] = [1,2,3] return(df) dff = pd.DataFrame() test(dff)
Так же, как Script 1, dff ---> objdff
. Во время test(dff)
переменная функции df ---> objdff
. Здесь все по-другому.
Операция (?) df['asdf'] = [1,2,3]
снова отправляется в базовый объект objdff
. В прошлый раз это привело к созданию нового объекта. Однако на этот раз операция ['asdf']
напрямую редактирует объект objdff
. Таким образом, объект objdff
имеет дополнительный столбец "asdf".
Поэтому в конце мы имеем df ---> objdff
и df ---> objdff
dff ---> objdff
. Поэтому они указывают на один и тот же объект, что означает, что переменная dff
указывает на отредактированный объект.
Как только мы objdff
функцию, переменная dff
прежнему указывает на objdff
, в которой есть новые данные. Это дает нам желаемый результат.
import pandas as pd def test(df): df = df.append({'asdf':1, 'sdf':2}, ignore_index=True) return(df) dff = pd.DataFrame() dff = test(dff)
Этот скрипт точно идентичен сценарию 1, за исключением dff = test(dff)
. Мы займемся этим через секунду.
Продолжая с конца сценария 1, мы остановились, когда завершился функциональный test(dff)
, и мы имеем dff ---> objdff
и df ---> objdff_apnd
.
Функция test
имеет return
команду, и поэтому возвращает объект objdff_apnd
. Это превращает строку dff = test(dff)
в dff = objdff_apnd
.
Поэтому в конце мы имеем dff ---> objdff_apnd
, что является именно тем результатом, который мы хотим.
append
возвращает новый объект, и, следовательно, он не заполняет исходный фреймворк.Проверьте этот пример:
def test1(a):
a.append(1)
def test2(a):
a = [1, 2, 3]
def test3(a):
a[0] = 10
aa = list()
test1(aa)
print(aa)
aa = list()
test2(aa)
print(aa)
aa = list([1])
test3(aa)
print(aa)
Выход:
[1]
[]
[10]
append
Dataframe: DataFrame.append(other, ignore_index=False, verify_integrity=False, sort=None)[source] Append rows of other to the end of this frame, returning a new object. Columns not in this frame are added as new columns.
append
возвращает новый объектglobal
ключевое слово не так... Я думаю, что даже если у вас нет global
в первом сценарии, все равно это не будет иметь никакого значения... Я не подробности о global
ключевом слове, так что я не буду упомянуть что-нибудь об этом. Но я знаю, как использовать ключевое слово, и это определенно не правильный способ его использования