Можно ли в python изменить переменную переданную в аргементе функции
В функцию передается не переменная, а ее значение, это не указатели. Просто верните новое значение из функции a = func(a) , в функцию добавить return a . Другой вариант, передавать в параметр функции что-нибудь сложное, типа объекта класса или контейнер (список, словарь, . )
26 авг 2021 в 8:25
Переменная a, которой присваивается значение «victor», и переменная а, которая в функции, это две совершенно разные вещи. Общего у них только имя. Любое из них можно изменить на какое угодно другое и в выполнении программы совершенно ничего не изменится. Так же, как у Бориса из Новосибирска и Бориса из Симферополя общего только имя Борис. А все остальное разное.
26 авг 2021 в 8:41
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Почему значение a не меняется?
Т.к. a — строка, то в функцию передаётся копия значения a , а не указатель на неё, соответственно, когда мы меняем значение a в функции, мы меняем значение локальной копии, а не глобальной переменной.
def func(a): a = "anton" print("внутри функции", id(a)) a = "victor" print("вне функции", id(a)) func(a) print("вне функции", id(a)) print(a)
Выведет что-то типо
вне функции 485256706736 внутри функции 485257302960 вне функции 485256706736 victor
id показывает место в памяти, где лежит значение переменной и, как видите, вне функции оно одно, а внутри — другое.
Как можно обойти?
В вашем случае вы можете использовать ключевое слово global , чтобы получить и изменить значение глобальной переменной:
def func(): global a a = "anton" print("внутри функции", id(a)) a = "victor" print("вне функции", id(a)) func() print("вне функции", id(a)) print(a)
Выведет что-то на подобии:
вне функции 542195432176 внутри функции 542200565424 вне функции 542200565424 anton
Как лучше реализовать?
Метод с использованием global — наверняка не лучший способ достичь результата, который вы хотите, и я бы посчитал бы это code smell-ом, потому я предлагаю вам сделать так:
def func(a): a = "anton" print("внутри функции", id(a)) return a a = "victor" print("вне функции", id(a)) a = func(a) print("вне функции", id(a)) print(a)
Выведет что-то такое:
вне функции 486729169712 внутри функции 486730096368 вне функции 486730096368 anton
Этот подход намного лучше первого.
Что за локальные и глобальные переменные?
Локальные переменные — те, которые «заперты» в каком-то контексте, например:
def func(arg): pass
Здесь arg — локальная переменная, она существует только в контексте функции func , и имеет влияние только на этот контекст.
Глобальные переменные — те, котоые мы объявляем в контексте программы:
foo = 5
Переменная foo — глобалья, т.к. объявлена в контексте программы (модуля, или же без отступа)
В Питоне переменные передаются по ссылке или по значению? Есть подводные камни?
Неизменяемые объекты передаются по значению. Это значит, что при изменении значения переменной будет создан новый объект. К этому типу относятся:
- числовые данные (int, float, complex)
- символьные строки (str)
- кортежи (tuple) При инициализации переменной незменяемого типа создается объект (например, целое число), этот объект имеет некоторый идентификатор:
>>> a = 10 >>> id(a) 10914656
оператор = связывает переменную a и объект посредством ссылки. При этом вы не можете изменить сам объект, т.е. когда вы присвоите переменной новое значение, интерпретатор создаст новый объект (если до этого этот объект был создан, то переменная просто получит ссылку), а первоначальный объект удалится из памяти сбощиком мусора, если ссылок на него больше нет.
Изменяемые (mutable)
Изменяемые объекты передаются по ссылке. Это значит, что при изменении значения переменной объект будет изменен. К этому типу относятся:
- списки (list)
- множества (set)
- словари (dict)
Подводные камни
Создадим список a , установим для переменной b ссылку на a , прибавим к b элемент списка и выведем их значения и идентификаторы на экран:
>>> a = [1, 2] >>> b = a >>> b.append(3) >>> print(a, b) [1, 2, 3] [1, 2, 3] >>> print(id(a), id(b)) 139748057891656 139748057891656
Как мы видим, переменные имеют одинаковые id и элементы списка. Если ты не знаешь об этой особенности изменяемых объетов, то такое поведение программы для тебя становится полной неожиданностью и может привести к ошибке в работе программы. Таким же образом с помощью ссылки на изменяемый объект, переменная передается в функцию:
>>> def add_value(a): . a.append(3) >>> b = [1, 2] >>> add_value(b) >>> print(b) [1, 2, 3]
Даже возвращая None , функция изменила список b , чего бы нам не хотелось.
Что с этим можно сделать
Для того, чтобы передать в функцию изменяемую переменную как значение, нужно сделать копию изменяемого элемента. Создадим копию списка:
новый_лист = старый_лист[:]
Тоже самое можем сделать вот так:
новый_лист = list(старый_лист)
Переменная новый_лист ссылается на новый объект:
>>> id(старый_лист), id(новый_лист) (139748050279112, 139748057891656)
Это дает нам возможность изменять оба объекта независимо друг от друга.
Что почитать
- Переменные-ссылки в Python
- Understanding Python variables and Memory Management
Попробуйте бесплатные уроки по Python
Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.
Переходите на страницу учебных модулей «Девмана» и выбирайте тему.
Обмен значений переменных

Если попытаться сделать такой обмен, сначала присвоив первой переменной значение второй, а второй — значение первой, ничего не получится. Допустим, есть две переменные a и b . При этом a = 5 и b = 6 . Если выполнить выражение a = b , то переменная a будет хранить число 6, также как и b . Число 5 будет утеряно. После этого выражение b = a теряет смысл, так как b будет присвоено его же текущее значение. В данном случае 6.
Поэтому в программировании при обмене значений переменных обычно вводят третью, играющую роль буфера (буферная переменная). В ней сохраняют значение первой переменной, потом первой присваивают значение второй, a новое значение для второй берут из буфера.
Таким образом классический алгоритм обмена значений двух переменных выглядит так:
a = 5 b = 6 buf = a a = b b = buf
Полная версия программы, которая запрашивает ввод двух значений с клавиатуры:
a = input('a: ') b = input('b: ') buf = a a = b b = buf print('a =', a) print('b =', b)
Пример выполнения программы:
a: 91 b: 162 a = 162 b = 91
Есть другой способ, позволяющий не использовать третью переменную, — арифметический. Он подходит только для чисел.
a = 5 b = 7 a = a + b # 12 b = a - b # 12-7=5 a = a - b # 12-5=7
a = int(input('a: ')) b = int(input('b: ')) a = a + b b = a - b a = a - b print('a =', a) print('b =', b)
Третий вариант — использовать имеющееся в Python множественное присваивание. При этом обмен значений переменных выполняется одной строкой кода:
>>> a = 10 >>> b = 20 >>> a, b = b, a >>> a 20 >>> b 10
Почему такое возможно? В Питоне есть такая структура данных как кортеж.
При выполнении a, b = b, a интерпретатор Python сначала получает значения связанные с переменными b и a (правая часть) и помещает их в кортеж, в данном случае получится (10, 20) . После этого он связывает каждый элемент кортежа в определенной позиции с переменными в той же позиции, но расположенными слева.
Таким образом можно поменять значения не только двух переменных, также трех и более. Кроме того в Python можно обменивать значения переменных разных типов. Это связано с тем, что тип данных в Питоне привязан не к переменной, а к значению:
>>> a = 14 >>> b = 18 >>> c = 16 >>> a,b,c = c,a,b >>> a,b,c (16, 14, 18) >>> d = 4.56 >>> e = "hi" >>> d,e = e,d >>> d 'hi' >>> e 4.56
X Скрыть Наверх
Решение задач на Python
Функции подробнее
К функции можно присоединить атрибут. В них можно сохранять информацию.
def func(): . # Создает объект функции func() # Вызывает объект func.attr = value # Присоединяет атрибут attr к объекту и записывает в него значение value
Ничего не возвращает
Не обязательно в фукнции писать return (или yield). Функция может ничего не возвращать.
Результат вызова такой функции None
>>> def hi(name): . print('Hello,', name) . >>> x = hi('Mike') Hello, Mike >>> x None
Полиморфизм
Создание функцию:
>>> def times(x, y): # Создать функцию и связать ее с именем . return x * y #Тело, выполняемое при вызове функции
Вызов функции:
>>> times(2, 4) 8 >>> times('Hi', 3) HiHiHi
Одна и та же функция times используется для разного: умножения чисел и повторения последовательностей.
Полиморфизм означает, что смысл операций зависит от типов операндов.
Динамическая типизация и полиморфизм — основа языка питон. Не нужно в функциях писать ограничения на типы. Либо операция поддерживает тип операнда, либо автоматически возбуждается исключение. Не ломайте этот механизм.
Возможный минус — несовпадение типов обнаруживается только при выполнении (и только если этот код вызван). Покрывайте код тестами.
Функция ищет пересечение двух последовательностей:
def intersect(seq1, seq2): res = [] for x in seq1: if x in seq2: res.append(x) return res
Заметим, что в функции первый аргумент должен быть итерируемым (с ним дожен работать for), а воторой аргумент — поддерживать оператор in. Других ограничений на объекты нет.
Этот же код (как будет рассказано дальше), можно записать в виде генератора списков:
[x for x in seq1 if x in seq2]
Локальные переменные
Локальная переменная — имя, которое доступно только внутри инструкции def и существует только во время выполнения функции (после окончания функции эти переменные уничтожаются).
def intersect(seq1, seq2): res = [] for x in seq1: if x in seq2: res.append(x) return res
Здесь локальные переменные:
- res — потому что ей присваивают;
- аргументы передаются через операцию присваивания, поэтому seq1 и seq2 — локальные переменные;
- цикл for присваивает значения переменной x, поэтому она тоже локальная переменная.
Они появляются при вызове фукнции и исчезают после ее окончания.
Область видимости (пространство имен, namespace)
Область видимости (пространство имен) — область, где хранятся переменные. Здесь определяются переменные и делают поиск имен.
Операция = связывает имена с областью видимости (пространством имен)
Каждая функция добавляет свое пространство имен:
- имена внутри def видны только внутри def.
- можно делать одинаковые имена (x) в разных простанствах имен (другом def или во всем модуле).
- локальная переменная — присвоение внутри def; видна только внутри def.
- нелокальная переменная — в пределах объемлющей инструкции def; там и видна.
- глобальная переменная — вне всех def; глобальная для всего файла.
Лексическая область видимости — потому что определяется тем, где первый раз было =.
Модули и функции
- Модуль — глобальная область видимости
- пространство имен, где создаются переменные на верхнем уровне в файле модуля.
- глобальные переменные с точки зрения:
- внешнего мира — это атрибуты модуля;
- внутри модуля — это обычные переменные.
- слышим «глобальный» подразумеваем «один модуль», более глобального в питоне нет.
- рекурсия — основывается на множественности (и разных!) областях видимости.
- иначе используем global, nonlocal
- Не было присвоения? Ищи в каком пространстве имен эта переменная!
- объемлющей локальной области видимости (внутри объемлющей инструкции def);
- глобальной (в пространстве имен модуля);
- встроенной (предопределенные имена в модуле buildins)
Присвоил? Определил область локальности.
Операции непосредственого изменения объекта — это НЕ присвоение! Они не делают переменную локальной.
x.append(7) # остается глобальной y = [1, 2, 3] # y стала локальной в текущем пространствеРазрешение имен: правило LEGB
Для инструкции def:
- Поиск имен ведется самое большее в четырех областях видимости: локальной, затем в объемлющей функции (если таковая имеется), затем в глобальной и, наконец, во встроенной.
- По умолчанию операция присваивания создает локальные имена.
- Объявления global и nonlocal отображают имена на область видимости вмещающего модуля и функции соответственно.
- Когда внутри функции выполняется обращение к неизвестному имени, интерпретатор пытается отыскать его в четырех областях видимости – в локальной (local, L), затем в локальной области любой объемлющей инструк- ции def (enclosing, E) или в выражении lambda, затем в глобальной (global, G) и, наконец, во встроенной (built-in, B).
- Поиск завершается, как только будет найдено первое подходящее имя.
- Если требуемое имя не будет найдено, интерпретатор выведет сообщение об ошибке.

Это правила поиска имен переменных. Для атрибутов объектов применяются другие правила (см. Наследование).
# Глобальная область видимости X = 99 # X и func определены в модуле: глобальная область def func(Y): # Y и Z определены в функции: локальная область # Локальная область видимости Z = X + Y # X – глобальная переменная return Z func(1) # func в модуле: вернет число 100- Глобальные имена: X, func (так как объявлены на верхнем уровне модуля)
- Локальные имена: Y (аргументы передаются через присвоение), Z (создается через =)
x = 88 # глобальная переменная x def func(): x = 1 # создали локальную переменную x, переопределяет глобальную func() print(x) # 88, печатаем глобальную переменнуюВстроенная область видимости (biuldins)
В действительности, встроенная область видмости – это всего лишь встроенный модуль с именем builtis, но для того, чтобы использовать имя builtis, необходимо импортировать модуль builtis, потому что это имя само по себе не является встроенным.
>>> import builtins >>> dir(builtins) ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', . множество других имен опущено. 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']Использование встроенных функций:
>>> zip # Обычный способ >>> import builtins # Более сложный способ >>> builtins.zip
НЕ переопределяйте встроенные имена!
def hider(): open = 'spam' # Локальная переменная, переопределяет встроенное имя . open('data.txt') # В этой области видимости файл не будет открыт!global и nonlocal
global и nonlocal НЕ объявляет переменную, а определяет пространство имен переменной. Позволяет изменять переменные за пределами текущей def.
Глобальные имена – это имена, которые определены на верхнем уровне вмещающего модуля.
- читать такое имя можно и без global;
- global нужно, чтобы присваивать глобальным именам.
Нелокальные имена — внешние для текущей def, но не верхний уровень модуля.
X = 88 # Глобальная переменная X def func(): global X X = 99 # Глобальная переменная X: за пределами инструкции def func() print(X) # Выведет 99Здесь все переменные тоже глобальные:
y, z = 1, 2 # Глобальные переменные в модуле def all_global(): global x # Объявляется глобальной для присваивания x = y + z # Объявлять y, z не требуется: применяется правило LEGBЕсли x не существовала в момент вызова функции, то операция = создаст переменную х в области видимости модуля
Меньше глобальных переменных
Почему нужно стремиться делать меньше глобальных переменных?
Пусть мы хотим модифицировать этот модуль или использовать в другой программе. Чему равно х? Это зависит от того, какие функции вызывались. Сложнее понять код, ибо нужно знать как выполняется ВСЯ программа (кого вызывали последней — func1 или func2, или их вообще не вызывали).
X = 99 def func1(): global X X = 88 def func2(): global X X = 77В многопоточном программировании глобальные переменные используют в качестве общей памяти для разных потоков (т.е. средства связи между потоками).
Не изменяйте глобальные переменные непосредственно в других модулях
Пусть сначала написали модуль first.py, потом написали модуль second.py и далее пытаемся понять, как работает программа. Изменение переменных модуля в другом модуле напрямую ухудщает читаемость кода.
# first.py X = 99 # в first.py не знаем о second.py # second.py import first print(first.X) # читать - хорошо first.X = 88 # изменять - плохоПрограммист, поддерживающий first.py пытается найти кто его импортировал (и в какой директории) и изменил.
Меньше связей между модулями — проще поддерживать и изменять модули.
Лучше делать связи через вызовы функций и возвращаемые значения (проще контролировать).
# first.py X = 99 def setX(new): # интерфейс модуля global X X = new # second.py import first first.setX(88)Вложенные области видимости
Чтение переменной внутри функции. Ищем имя:
- в локальной области видимости функции;
- в локальных областях видимости объемлющих функций изнутри наружу;
- в глобальной области видимости модуля;
- в builtins (встроенная область видимости).
x = value внутри функции:
- создает или изменяет имя х в текущей локальной области видимости функции;
- если был unlocal x , то = создает или изменяет имя в ближайшей области видимости объемлющей функции.
- если был global x , то = создает или изменяет имя в области видимости объемлющего модуля.
X = 99 # Имя в глобальной области видимости: не используется def f1(): X = 88 # Локальное имя в объемлющей функции def f2(): print(X) # Обращение к переменной во вложенной функции f2() f1() # Выведет 88: локальная переменная в объемлющей функцииИщем в объемлющих областях, даже если объемлющая функция фактически уже вернула управление:
def f1(): X = 88 def f2(): print(X) # Сохраняет значение X в объемлющей области видимости return f2 # Возвращает f2, но не вызывает ее action = f1() # Создает и возвращает функцию action() # Вызов этой функции: выведет 88Функция f2 помнит переменную Х в области видимости объемлющей функции f1, которая уже неактивна.
Замыкание (фабричная функция)
Объект функции, который сохраняет свое значение в объемлющей области видимости, даже когда эти области перестали существовать.
(Для сохранения состояний лучше использовать классы).
Например, фабричные функции иногда используются в программах, когда необходимо создавать обработчики событий прямо в процессе выполнения, в соответствии со сложившимися условиями (например, когда желательно запретить пользователю вводить данные).
Определим внешнюю фукнцию, которая создает и возвращает внутреннюю функцию, не вызывая ее.
>>> def maker(N): . def action(X): # Создать и вернуть функцию . return X ** N # Функция action запоминает значение N в объемлющей . return action # области видимости .Вызовем внешнюю функцию. Она возвращает ссылку на созданную ею вложенную функцию, созданную при выполнении вложенной инструкции def:
>>> f = maker(2) # Запишет 2 в N >>> f 0x014720B0>Вызовем по этой ссылке функцию (внутреннюю!):
>>> f(3) # Запишет 3 в X, в N по-прежнему хранится число 2 9 >>> f(4) # 4 ** 2 16Вложенная функция продолжает хранить число 2, значение переменной N в функции maker даже при том, что к моменту вызова функции action функция maker уже завершила свою работу и вернула управление. В действительности имя N из объемлющей локальной области видимости сохраняется как информация о состоянии, присоединенная к функции action, и мы получаем обратно значение аргумента, возведенное в квадрат. Теперь, если снова вызвать внешнюю функцию, мы получим новую вложенную функцию уже с другой информацией о состоянии, присоединенной к ней, — в результате вместо квадрата будет вычисляться куб аргумента, но ранее со храненная функция по-прежнему будет возвращать квадрат аргумента:
>>> g = maker(3) # Функция g хранит число 3, а f – число 2 >>> g(3) # 3 ** 3 27 >>> f(3) # 3 ** 2 9при каждом обращении к фабричной функции, как в данном примере, произведенные ею функции сохраняют свой собственный блок данных с информацией о состоянии. В нашем случае благодаря тому, что каждая из функций получает свой собственный блок данных с информацией о состоянии, функция, которая присваивается имени g, запо минает число 3 в переменной N функции maker, а функция f – число 2.
Лучше для хранения информации подходят классы или глобальные переменные.
Избавьтесь от вложенности
Плоское лучше вложенного — один из принципов питона.
def f1(): X = 88 def f2(): print(X) # Сохраняет значение X в объемлющей области видимости return f2 # Возвращает f2, но не вызывает ее action = f1() # Создает и возвращает функцию action() # Вызов этой функции: выведет 88Напишите лучше без вложенных функций (делает то же самое):
>>> def f1(): . x = 88 # Передача значения x вместо вложения функций . f2(x) # Опережающие ссылки считаются допустимыми . >>> def f2(x): . print(x) . >>> f1() 88Вложенные области видимости и lambda-выражения
lambda (как и def) порождает новую область видимости.
def func(): x = 4 action = (lambda n: x ** n) # запоминается x из объемлющей инструкции def return action x = func() print(x(2)) # Выведет 16, 4 ** 2То же самое, с ручной передачаей значения (работет и в старых версиях питона):
def func(): x = 4 action = (lambda n, x=x: x ** n) # Передача x вручную return actionКогда нужно передать значение в lambda вручную? Если мы используем lambda в цикле. Хотим, чтобы каждая lambda запомнила свое значение i. Получилось, что все lambda запомнили последнее значение i (4).
>>> def makeActions(): . acts = [] . for i in range(5): # Сохранить каждое значение i . acts.append(lambda x: i ** x) # Все запомнят последнее значение i! . return acts . >>> acts = makeActions() >>> acts[0] lambda> at 0x012B16B0> >>> acts[0](2) # Все возвращают 4 ** 2, последнее значение i 16 >>> acts[2](2) # Здесь должно быть 2 ** 2 16 >>> acts[4](2) # Здесь должно быть 4 ** 2 16Поиск переменной в объемлющей области видимости производится позднее, при вызове вложенных функций, в результате все они получат одно и то же значение (значение, которое имела переменная цикла на последней итерации). То есть каждая функция в списке будет возвращать 4 во второй степени, потому что во всех них переменная i имеет одно и то же значение.
Надо явно сохранять значение из объемлющей области видимости в виде аргумента со значением по умолчанию вместо использования ссылки на переменную из объемлющей области видимости. Значения по умолчанию вычисляются в момент создания вложенной функции (а не когда она вызывается), поэтому каждая из них сохранит свое собственное значение i:
>>> def makeActions(): . acts = [] . for i in range(5): # Использовать значения по умолчанию . acts.append(lambda x, i=i: i ** x) # Сохранить текущее значение i . return acts . >>> acts = makeActions() >>> acts[0](2) # 0 ** 2 0 >>> acts[2](2) # 2 ** 2 4 >>> acts[4](2) # 4 ** 2 16На подобный эффект можно натолкнуться в коде, которые генерируют функции-обработчики событий для GUI.
nonlocal — «пропустить локальную область видимости при поиске имен»
Для чтения значений переменных не нужно. Только для =.
- global
- вынуждает интерпретатор начинать поиск имен с области объемлю- щего модуля;
- позволяет присваивать переменным новые значения.
- Область поиска простирается вплоть до встроенной области видимости, если искомое имя не будет найдено в модуле,
- при этом операция присваивания значений глобальным именам всегда будет создавать или изменять переменные в области видимости модуля.
- ограничивает область поиска областями видимости объемлющих функций;
- требует, чтобы перечисленные в инструкции имена уже существовали,
- позволяет присваивать им новые значения;
- в область поиска не входят глобальная и встроенная области видимости.
>>> def tester(start): . state = start # Обращение к нелокальным переменным действует как обычно . def nested(label): . print(label, state) # Извлекает значение state из области видимости объемлющей функции . state += 1 # создается ЛОКАЛЬНАЯ переменная - хотели изменить нелокальную . return nested . >>> F = tester(0) >>> F('spam') spam 0 >>> F('ham') ham 0По умолчанию нельзя изменять значения переменной объемлющей области видимости
unlocal — позволяет изменять, даже если функция tester завершила работу к моменту вызова функции nested через переменню F:
>>> def tester(start): . state = start # В каждом вызове сохраняется свое значение state . def nested(label): . nonlocal state # Объект state находится . print(label, state) # в объемлющей области видимости . state += 1 # Изменит значение переменной, объявленной как nonlocal . return nested . >>> F = tester(0) >>> F('spam') # Будет увеличивать значение state при каждом вызове spam 0 >>> F('ham') ham 1 >>> F('eggs') eggs 2Напоминаем, каждый вызов фабричной фукнции tester будет создавать отдельную копию переменной state. Объект state, находящийся в объемлющей области видимости, фактически прикрепляется к возвращаемому объекту функции nested — каждый вызов функции tester создает новый, независимый объект state, благодаря чему изменение state в одной функции не будет оказывать влияния на другие. Вызываем еще эти же функции:
>>> G = tester(42) # Создаст новую функцию, которая начнет счет с 42 >>> G('spam') spam 42 >>> G('eggs') # Обновит значение state до 43 eggs 43 >>> F('bacon') # Но в функции F значение state останется прежним bacon 3 # Каждая новая функция получает свой экземпляр stateunlocal переменная должна уже существовать (global — не обязательно, создаем новую)
>>> def tester(start): . def nested(label): . nonlocal state # Нелокальные переменные должны существовать! . state = 0 . print(label, state) . return nested . SyntaxError: no binding for nonlocal 'state' found >>> def tester(start): . def nested(label): . global state # Глобальные переменные могут отсутствовать . state = 0 # Создаст переменную в области видимости модуля . print(label, state) . return nested . >>> F = tester(0) >>> F(‘abc’) abc 0 >>> state 0unlocal область видимости — без глобальной области модуля или built-in
>>> spam = 99 >>> def tester(): . def nested(): . nonlocal spam # Переменная должна быть внутри def, а не в модуле! . print('Current=', spam) . spam += 1 . return nested . SyntaxError: no binding for nonlocal 'spam' foundИнтерпретатор определяет местоположение нелокальных имен в момент создания функции, а не в момент ее вызова.
Чем заменить unlocal?
Глобальная переменная
Минус — один (единственный) экземпляр переменной для хранения информации.
global нужно написать в обеих функциях.
>>> def tester(start): . global state # Переместить в область видимости модуля . state = start # global позволяет изменять переменные, находящиеся . def nested(label): # в области видимости модуля . global state . print(label, state) . state += 1 . return nested . >>> F = tester(0) >>> F('spam') # Каждый вызов будет изменять глобальную state spam 0 >>> F('eggs') eggs 1 >>> G = tester(42) # Сбросит значение единственной копии state >>> G('toast') # в глобальной области видимости toast 42 >>> G('bacon') bacon 43 >>> F('ham') # Ой – значение моего счетчика было затерто! ham 44Классы
Код лучше читается!
>>> class tester: # Альтернативное решение на основе классов . def __init__(self, start): # Конструктор объекта, . self.state = start # сохранение информации в новом объекте . def nested(self, label): . print(label, self.state) # Явное обращение к информации . self.state += 1 # Изменения всегда допустимы . >>> F = tester(0) # Создаст экземпляр класса, вызовет __init__ >>> F.nested('spam') # Ссылка на F будет передана в аргументе self spam 0 >>> F.nested('ham') ham 1 >>> G = tester(42) # Каждый экземпляр получает свою копию информации >>> G.nested('toast') # Изменения в одном объекте не сказываются на других toast 42 >>> G.nested('bacon') bacon 43 >>> F.nested('eggs') # В объекте F сохранилась прежняя информация eggs 2 >>> F.state # Информация может быть получена за пределами класса 3Избавимся от функции nested, чтобы мы могли писать прямо F(‘spam’), переопределив функцию __call__
>>> class tester: . def __init__(self, start): . self.state = start . def __call__(self, label): # Вызывается при вызове экземпляра . print(label, self.state) # Благодаря этому отпадает . self.state += 1 # необходимость в методе .nested() . >>> H = tester(99) >>> H('juice') # Вызовет метод __call__ juice 99 >>> H('pancakes') pancakes 100Атрибуты функций
>>> def tester(start): . def nested(label): . print(label, nested.state) # nested – объемлющая область видимости . nested.state += 1 # Изменит атрибут, а не значение имени nested . nested.state = start # Инициализация после создания функции . return nested . >>> F = tester(0) >>> F('spam') # F – это функция 'nested' spam 0 # с присоединенным атрибутом state >>> F('ham') ham 1 >>> F.state # Атрибут state доступен за пределами функции 2 >>> >>> G = tester(42) # G имеет собственный атрибут state, >>> G('eggs') # отличный от одноименного атрибута функции F eggs 42 >>> F('ham') ham 2имя функции nested является локальной переменной в области видимости функции tester, включающей имя nested, – на это имя можно ссылаться и внутри функции nested. Кроме того, здесь используется то обстоятельство, что изменение самого объекта не является операцией присваивания, — операция увеличения значения nested.state изменяет часть объекта, на который ссылается имя nested, а не саму переменную с именем nested. Поскольку во вложенной функции не выполняется операция присваивания, необходимость в инструкции nonlocal отпадает сама собой.
Контрольные вопросы
- Что выведет следующий фрагмент и почему?
>>> X = 'Spam' >>> def func(): . print(X) . >>> func()>>> X = 'Spam' >>> def func(): . X ='NI!' . >>> func() >>> print(X)>>> X = 'Spam' >>> def func(): . X = 'NI' . print(X) . >>> func() >>> print(X)>>> X = 'Spam' >>> def func(): . global X . X = 'NI' . >>> func() >>> print(X)>>> X = 'Spam' >>> def func(): . X = 'NI' . def nested(): . print(X) . nested() . >>> func() >>> X>>> def func(): . X = 'NI' . def nested(): . nonlocal X . X = 'Spam' . nested() . print(X) . >>> func()Ответы
- В данном случае будет выведена строка ‘Spam’, потому что функция обращается к глобальной переменной в объемлющем модуле (если внутри функции переменной не присваивается значение, она интерпретируется как глобальная).
- В данном случае снова будет выведена строка ‘Spam’, потому что операция присваивания внутри функции создает локальную переменную и тем самым скрывает глобальную переменную с тем же именем. Инструкция print находит неизмененную переменную в глобальной области видимости.
- Будет выведена последовательность символов ‘Ni’ в одной строке и ‘Spam’ — в другой, потому что внутри функции инструкция print найдет локальную переменную, а за ее пределами – глобальную.
- На этот раз будет выведена строка ‘Ni’, потому что объявление global предписывает выполнять присваивание внутри функции переменной, находящейся в глобальной области видимости объемлющего модуля.
- В этом случае снова будет выведена последовательность символов ‘Ni’ в одной строке и ‘Spam’ – в другой, потому что инструкция print во вложенной функции отыщет имя в локальной области видимости объемлющей функции, а инструкция print в конце фрагмента отыщет имя в глобальной области видимости.
- Этот фрагмент выведет строку ‘Spam’, так как инструкция nonlocal (доступная в Python 3.0, но не в 2.6) означает, что операция присваивания внутри вложенной функции изменит переменную X в локальной области видимости объемлющей функции. Без этой инструкции операция присваивания классифицировала бы переменную X как локальную для вложенной функции и создала бы совершенно другую переменную — в этом случае приведенный фрагмент вывел бы строку ‘NI’.
- Поскольку значения локальных переменных исчезают, когда функция возвращает управление, то информацию о состоянии в языке Python можно сохранять в глобальных переменных, для вложенных функций — в области видимости объемлющих функций, а также посредством аргументов со значениями по умолчанию. Иногда можно использовать прием, основанный на сохранении информации в атрибутах, присоединяемых к функциям, вместо использования области видимости объемлющей функции. Альтернативный способ заключается в использовании классов и приемов ООП, который обеспечивает лучшую поддержку возможности сохранения ин- формации о состоянии, чем любой из предыдущих приемов, основанных на использовании областей видимости, потому что этот способ делает сохранение явным, позволяя выполнять присваивание значений атрибутам.
results matching » «
No results matching » «