Я пытаюсь понять подход Python к переменной области. В этом примере, почему f()
может изменить значение x
, как воспринимается внутри main()
, но не значение n
?
def f(n, x):
n = 2
x.append(4)
print('In f():', n, x)
def main():
n = 1
x = [0,1,2,3]
print('Before:', n, x)
f(n, x)
print('After: ', n, x)
main()
Вывод:
Before: 1 [0, 1, 2, 3]
In f(): 2 [0, 1, 2, 3, 4]
After: 1 [0, 1, 2, 3, 4]
Ответы:
Некоторые ответы содержат слово «копировать» в контексте вызова функции. Я нахожу это запутанным.
Python не копирует объекты вы проходите во время вызова функции когда - либо .
Параметры функции являются именами . Когда вы вызываете функцию, Python связывает эти параметры с любыми передаваемыми вами объектами (через имена в области вызова).
Объекты могут быть изменяемыми (например, списки) или неизменяемыми (например, целые числа, строки в Python). Изменчивый объект вы можете изменить. Вы не можете изменить имя, вы можете просто привязать его к другому объекту.
Ваш пример касается не областей или пространств имен , а именования, привязки и изменчивости объекта в Python.
Вот хорошие картинки о разнице между переменными в других языках и именами в Python .
источник
def foo(x, l=None): l=l or []; l.append(x**2); return l[-1]
.x = []
inf()
не влияет на списокx
в основной функции. Я обновил комментарий, чтобы сделать его более конкретным.У вас уже есть несколько ответов, и я в целом согласен с Дж. Ф. Себастьяном, но вы можете найти это полезным в качестве ярлыка:
Каждый раз, когда вы видите
varname =
, вы создаете новую привязку имени в области действия функции. Какое бы значение ниvarname
было связано до этого, оно теряется в этой области .Каждый раз, когда вы видите,
varname.foo()
вы вызываете методvarname
. Метод может изменить varname (напримерlist.append
).varname
(или, скорее, объект сvarname
именами) может существовать в нескольких областях, и, поскольку это один и тот же объект, любые изменения будут видны во всех областях.[обратите внимание, что
global
ключевое слово создает исключение для первого случая]источник
f
фактически не изменяет значениеx
(которое всегда является одной и той же ссылкой на экземпляр списка). Скорее, это изменяет содержимое этого списка.В обоих случаях копия ссылки передается в функцию. Внутри функции,
n
получает новое значение. Изменяется только ссылка внутри функции, а не та, что за ее пределами.x
не присваивается новое значение: ни ссылка внутри, ни снаружи функции не изменяются. Вместо этого,x
«s значение изменяется.Поскольку как
x
внутри функции, так и снаружи она ссылается на одно и то же значение, оба видят изменение. Напротив,n
внутренняя функция и внешняя часть ссылаются на разные значения послеn
переназначения внутри функции.источник
Я переименую переменные, чтобы уменьшить путаницу. n -> nf или nmain . x -> xf или xmain :
Когда вы вызываете функцию f , среда выполнения Python создает копию xmain и назначает ее для xf , а также назначает копию nmain для nf .
В случае n копируемое значение равно 1.
В случае x копируемое значение не является литеральным списком [0, 1, 2, 3] . Это ссылка на этот список. xf и xmain указывают на один и тот же список, поэтому при изменении xf вы также изменяете xmain .
Если, однако, вы должны были написать что-то вроде:
вы обнаружите, что xmain не изменился. Это потому, что в строке xf = ["foo", "bar"] вы изменили xf, чтобы указать на новый список. Любые изменения, внесенные вами в этот новый список, не окажут влияния на список, на который по- прежнему указывает xmain .
Надеюсь, это поможет. :-)
источник
nf = 2
имя неnf
будет изменено2
. Числа неизменны, списки изменчивы.Это потому, что список является изменяемым объектом. Вы не устанавливаете x в значение [0,1,2,3], вы определяете метку для объекта [0,1,2,3].
Вы должны объявить вашу функцию f () следующим образом:
источник
x = x + [4]
вместоx.append(4)
, вы бы не увидели никаких изменений в вызывающей стороне, хотя список является изменяемым. Это связано с тем, действительно ли оно мутировало.x += [4]
тоx
мутирует, точно так же, как это происходит с темx.append(4)
, чтобы вызывающий абонент увидел изменение.n - это int (неизменяемый), и копия передается функции, поэтому в функции вы изменяете копию.
X является списком (изменяемым), и копия указателя передается функции, поэтому x.append (4) изменяет содержимое списка. Однако, если вы сказали, что x = [0,1,2,3,4] в своей функции, вы не измените содержимое x в main ().
источник
Если функции переписаны с совершенно разными переменными, и мы называем их id , это хорошо иллюстрирует ситуацию . Сначала я этого не понял и прочитал пост jfs с отличным объяснением , поэтому попытался понять / убедить себя:
z и x имеют одинаковый идентификатор. Просто разные теги для той же базовой структуры, что и в статье.
источник
Python - это чистый язык передачи по значению, если вы думаете об этом правильно. Переменная python хранит местоположение объекта в памяти. Переменная Python не хранит сам объект. Когда вы передаете переменную в функцию, вы передаете копию адреса объекта, на который указывает переменная.
Сравните эти две функции
Теперь, когда вы вводите в оболочку
Сравните это с липучкой.
В первом случае мы передаем копию адреса cow в foo и foo изменяет состояние находящегося там объекта. Объект модифицируется.
Во втором случае вы передаете копию адреса коровы в Goo. Затем Goo приступает к изменению этой копии. Эффект: нет.
Я называю это принципом розового дома . Если вы сделаете копию своего адреса и скажете художнику раскрасить дом по этому адресу в розовый цвет, у вас получится розовый дом. Если вы дадите художнику копию своего адреса и скажете ему сменить его на новый, адрес вашего дома не изменится.
Объяснение устраняет много путаницы. Python передает адреса переменных, хранящиеся по значению.
источник
Python копируется по значению ссылки. Объект занимает поле в памяти, и ссылка связана с этим объектом, но сама занимает поле в памяти. И имя / значение связано со ссылкой. В функции python всегда копируется значение ссылки, поэтому в вашем коде n копируется в новое имя, и когда вы его присваиваете, в стеке вызывающей стороны появляется новое пространство. Но для списка также скопировано имя, но оно относится к той же памяти (поскольку вы никогда не назначаете списку новое значение). Это магия в питоне!
источник
Мое общее понимание состоит в том, что любая переменная объекта (например, список или dict) может быть изменена с помощью ее функций. Я полагаю, что вы не можете сделать это переназначить параметр - т.е. назначить его по ссылке в вызываемой функции.
Это согласуется со многими другими языками.
Запустите следующий короткий скрипт, чтобы увидеть, как он работает:
источник
Я изменил свой ответ множество раз и понял, что мне не нужно ничего говорить, Python уже объяснил.
Этот дьявол не является ссылкой / значением / изменяемым или нет / экземпляром, пространством имен или переменной / списком или строкой, ЭТО СИНТАКС, РАВНЫЙ ЗНАК.
источник