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