С переменные локальные: Локальные переменные | Программирование на C и C++
Содержание
Python 3 — глобальные и локальные переменные: область видимости функций
Область видимости переменных в языке программирования Python представляет собой некое пространство имен, в рамках которого функционируют созданные объекты. Эта особенность позволяет ограничивать доступ к определенным значениям во избежание конфликтов между одинаковыми идентификаторами. Переменные бывают двух видов: локальные и глобальные, что в большинстве случае определяется местом их первичной идентификации в программе.
Локальные переменные
Для создания переменных, обладающих локальной областью видимости, необходимо всего лишь поместить их в отдельный блок кода, изолированный от остальной программы. Чтобы увидеть локальную переменную в действии, достаточно инициализировать целочисленный объект с именем x и значением 100 в функции f, как это сделано в следующем примере:
def f(): x = 100 print(x) f() 100
Здесь x имеет локальную область видимости, так как доступна лишь в рамках своей функции f. Вызывая данную функцию из внешней части программы, можно увидеть вывод целочисленного значения на экране. Однако, если попытаться вывести переменную x при помощи метода print вне зоны действия функции f, компилятор тут же выдаст ошибку:
def f(): x = 100 print(x) f() print(x) 100 Traceback (most recent call last): File "main.py", line 5, in <module> print(x) NameError: name 'x' is not defined
Так происходит из-за того, что внешняя часть программы ничего не знает о переменной x, поскольку содержит в себе совсем другое пространство имен. Пользоваться локальными объектами можно только в той области, где они были идентифицированы. В обратном же случае компилятор сообщит об ошибке, не сумев обнаружить необходимую переменную.
Глобальные переменные
Чтобы иметь возможность использовать некоторое значение в любой части программы, следует объявить глобальную переменную. Для этого понадобиться создать переменную отдельно от области кода, ограниченной определенным блоком кода, например, функцией. В следующем примере демонстрируется идентификация целочисленного типа данных под названием x, который позже выводится на экран при помощи метода print в функции f:
x = 100 def f(): print(x) f() print(x) 100 100
Как можно заметить из результатов выполнения программы, значение 100 воспроизводится не только через f, но и с помощью обычного print. Таким образом, получение доступа к x осуществляется из любой части кода, благодаря глобальной области видимости подобного объекта. Но что будет, если попытаться изменить значение глобальной переменной в некой функции? Результаты такого эксперимента представлены в следующем фрагменте кода:
x = 100 def f(): x = 200 f() print(x) 100
Функция f присваивает значение 200 переменной с именем x, однако, вопреки ожиданиям, внешний метод print выводит число 100, которое принадлежало x изначально. Происходит так потому, что в данной программе создаются два разных объекта x с локальной, а также глобальной областью видимости. Исправить ситуацию поможет ключевое слово global:
x = 100 def f(): global x x = 200 f() print(x) 200
Пометив переменную x как global, можно обращаться к ее изначальному значению, которое было определено вне зоны действия функции f. Теперь после того как в x поместили число 200, вызов метода print выводит вполне ожидаемый результат, то есть измененное значение.
В Python глобальные переменные можно использовать для хранения каких то настроек программы или разрабатываемого модуля. Для этого хорошо подойдут словари.
Но все таки не стоит злоупотреблять. Зачастую гораздо правильнее передавать в функции необходимые значения в качестве аргуменов, а если нужно перезаписать какое-то глобальное значение, то возвращать его из функции.
def my_abs(val): return -val if val < 0 else val return val + 1 x = -15 x = my_abs(x)
Нелокальные переменные
Итак, для обращения к глобальной переменной внутри функции f необходимо использовать ключевое слово global перед ее идентификатором. Но что если требуется вызывать совсем не глобальную, а переменную, которая была определена во внешнем методе, являясь при этом локальной для другого пространства имен, находящегося на уровень выше? Следующий код демонстрирует попытку взаимодействия со значением из внешней функции f1 в методе f2:
def f1(): x = 100 def f2(): x = 200 f2() print(x) f1() 100
Несмотря на то, что переменной с таким же именем x было присвоено новое значение 200, в результате выполнения написанных методов на экране отобразилось 100. Как и в том случае с двумя разными переменными, локальной и глобальной, здесь имеется также два различных объекта, которые идентифицированы в отдельных блоках кода. Чтобы обратиться к объекту, который не является локальным, необходимо воспользоваться модификатором nonlocal:
def f1(): x = 100 def f2(): nonlocal x x = 200 f2() print(x) f1() 200
Таким образом, в методе f2 осуществляется запись значения 200 в переменную x из функции f1. В результате подобных действий, вызов метода f1 из внешней части программы создает новую переменную x, значение которой меняется в f2 со 100 на 200 и выводится при помощи print.
Важно заметить, что конструкция nonlocal была добавлена только в 3-ей версии языка Python.
Видимость из загружаемого модуля
Теперь разберемся с видимостью глобальных переменных между загружаемыми модулями Python. Например, мы подключаем другой модуль с помощью команды import. Создадим файл «test.py» и в него запишем следующий код:
print('Загружается модуль test') x = 100 def f(): print('Из функции x=' + str(x))
То есть мы определили глобальную переменную x для модуля test. Так же определили функцию, которая выводит на экран её значение.
Теперь создадим файл main.py, который и будем запускать. В нем мы импортируем модуль test, а так же создадим свою глобальную переменную x. После этого выведем значения глобальной переменной из test, вызовим функцию f, а так же проверим, что значение переменной в модуле main не изменилось:
x = 200 print('Из модуля main x=' + str(x)) import test print('Из модуля test x=' + str(test.x)) print('Присваиваем значение 300') test.x = 300; print('Из модуля test x=' + str(test.x)) test.f() print('Из модуля main x=' + str(x)) Из модуля main x=200 Загружается модуль test Из модуля test x=100 Присваиваем значение 300 Из модуля test x=300 Из функции x=300 Из модуля main x=200
Мы в первой же строчке записали в x значение 200. Это было сделано, чтобы показать, что после того, как мы загрузим внешний модуль, значение этой переменной не изменится. Так и вышло. Обращаясь к переменной из загруженной библиотеки, удалось прочитать его и изменить значение.
Теперь модифицируем программу следующим образом:
x = 200 print('Из модуля main x=' + str(x)) from test import * f() print('Из модуля main x=' + str(x)) print('Присваиваем значение 300') x = 300 f() print('Из модуля main x=' + str(x)) Из модуля main x=200 Загружается модуль test Из функции x=100 Из модуля main x=100 Присваиваем значение 300 Из функции x=100 Из модуля main x=300
В этом случае для загрузки мы использовали команду «from test import *». Мы импортировали все переменные и функции. После загрузки модуля значение переменной x в модуле main изменилось. Но при вызове функции, мы получаем значение x из модуля test. После присвоения нового значения переменной x, значение, которое выводит функция f не изменяется.
Следует по возможности избегать подключение библиотек с помощью команды from библиотека import *, а подключать только необходимые функции — from библиотека import функция. При этом надо удостовериться, что эти имена не используются в основном модуле.
Использование import библиотека поможет избежать возможных ошибок, если в программе есть функции, классы и переменные с такими же наименованиями, как и в загружаемом модуле.
Глобальные переменные в классе
Точно так же как и в функциях, можно обращаться к глобальным переменным и в классе Python. Разберем пример:
x = 100 print(x) class c1: global x x = 200 def __init__(self): global x x = 300 def f(self): global x x = 400 print(x) o1 = c1() print(x) o1.f() print(x) 100 200 300 400
Мы объявили глобальную переменную x. Вывели значение переменной до и после объявления класса. Как видим значение изменилось. После того как мы создали объект класса, значение в очередной раз поменялось. Это произошло, потому что сработал конструктор класса — метод __init__. После вызова функции f у созданного объекта, значение стало 400. В Python использование global переменная
и в функции класса, и в его конструкторе, и после описания класса дают возможность изменения глобальной переменной. Если убрать это объявление, то тогда выполнится присвоение локальной переменной.
Конечно же если мы определим локальную переменную в классе, то к ней не будет возможности доступа из другого класса:
class c1: x = 100 class c2: def f(self): print(x) o = c2() o.f() Traceback (most recent call last): File "main.py", line 7, in <module> o.f() File "main.py", line 5, in f print(x) NameError: name 'x' is not defined
Для того, чтобы код работал, переменная x должна быль глобальной.
Заключение
Таким образом, область видимости переменных в языке программирования Python, является важной составляющей платформы. Правильное взаимодействие со всеми ее особенностями позволяет избежать множества довольно сложных ошибок. Для более безопасного контроля над видимостью отдельных объектов применяются ключевые слова global и nonlocal. Чтобы ознакомиться с дополнительными сведениями по данной теме, следует изучить PEP 3104.
1.3.0 Локальные и глобальные переменные · Python Express Course
Подробнее о namespace читайте в следующем разделе. Здесь будет короткое описание основных моментов.
Область видимости (пространство имен) — область, где хранятся переменные. Здесь определяются переменные и делают поиск имен.
Операция = связывает имена с областью видимости (пространством имен)
Пока мы не написали ни одной функции, все переменные в программе глобальные.
Глобальные переменные видны во всех функциях программы. Они должны быть сначала созданы, а потом их можно читать и менять.
Создаются переменные присвоением =
Обычно пишут программу так:
a = 1
def f():
print(a)
f()
Этот код будет работать так же:
def f():
print(a)
a = 1
f()
Напечатает 1. Глобальная переменная а сначала была создана, а потом была вызвана функция f(). В функции f() видна глобальная переменная a.
Особенность интерптерируемого языка. Сначала — это раньше в процессе выполнения, а не «на строке с меньшим номером».
Локальная переменная создается внутри функции или блока (например, if или while).
Локальная переменная видна только внутри того блока (функции), где была создана.
def f():
a = 1
f()
print(a)
Ошибка «builtins.NameError: name ‘a’ is not defined»
- Локальные переменные создаются =.
- Каждый вызов функции создает локальную переменную (свою, новую) (каждый вызов функции создает свой новый namespace)
- после завершения функции ее локальные переменные уничтожаются.
- аргументы функции тоже являются локальными переменными (при вызове функции идет = параметру значения).
Итого: Если в функции было =, то мы создали локальную переменную. Если = не было, то читаем глобальную переменную.
Можно создавать в разных функциях локальные переменные с одинаковыми именами. В функциях foo и bar создали переменные с одинаковыми именами а.
Можно (но не надо так делать!) создавать локальную переменную с тем же именем, что и глобальную. pylint
поможет найти такие переменные.
def f():
a = 1
print(a, end=' ')
a = 0
f()
print(a)
Напечатает 1 0
.
- создается глобальная переменная а = 0
- вызывается f()
- в f создается локальная переменная а = 1 (теперь нельзя доступиться из функции f к глобальной переменной a)
- в f печатается локальная переменная a = 1
- завершается f
- печатается глобальная переменная а = 0
Переменная в функции будет считаться локальной, если она будет создана внутри условного оператора, который никогда не выполнится:
def f():
print(a)
if False:
a = 0
a = 1
f()
global говорит, что переменная относится к глобальному namespace. (В этот момент переменная НЕ создается). Переменную можно создать позже.
def f():
global a
a = 1
print(a, end=' ')
a = 0
f()
print(a)
выведет «1 1», т.к. значение глобальной переменной будет изменено внутри функции.
Рекурсивный вызов функции
Так как каждый вызов функции создает свое собственное пространство имен, можно писать функции рекурсивно.
Например, n! = n * (n-1)!, 0! = 1. Запишем это математическое определение факториала в виде кода.
def fact(n):
if n == 0:
return 1
return n * fact(n-1)
print(fact(5))
При вызове fact(5) создается namespace c n=5, далее идет вызов f(4) и создается еще один namespace, в нем n=4 (это другая переменная n, она в другом пространстве имен и та n=5 из этого пространства не доступна).
Вложенные области видимости
Можно определять одну функцию внутри другой.
Чтение переменной внутри функции. Ищем имя:
- в локальной области видимости функции;
- в локальных областях видимости объемлющих функций изнутри наружу;
- в глобальной области видимости модуля;
- в builtins (встроенная область видимости).
x = value
внутри функции:
- создает или изменяет имя х в текущей локальной области видимости функции;
- если был
unlocal x
, то = создает или изменяет имя в ближайшей области видимости объемлющей функции. - если был
global x
, то = создает или изменяет имя в области видимости объемлющего модуля.
X = 99
def f1():
X = 88
def f2():
print(X)
f2()
f1()
f2()
В f2() нельзя изменить значение Х, принадлежащей функции f1(). Вместо этого будет создана еще одна локальная переменная, но уже в пространстве имен функции f2().
Напечатает 77 88
:
X = 99
def f1():
X = 88
def f2():
X = 77
print(X)
f2()
print(X)
f1()
Если нужно изменять значение переменной Х, которая принадлежит пространству имен объемлющей (enclosed) функции, то добавляют unlocal
X = 99
def f1():
X = 88
def f2():
unlocal X
X = 77
print(X)
f2()
print(X)
f1()
При определении, к какому namespace относится имя, используют правило LEGB:
- Когда внутри функции выполняется обращение к неизвестному имени, интерпретатор пытается отыскать его в четырех областях видимости – в локальной (local, L), затем в локальной области любой объемлющей инструк-
ции def (enclosing, E) или в выражении lambda, затем в глобальной (global, G) и, наконец, во встроенной (built-in, B).- Поиск завершается, как только будет найдено первое подходящее имя.
- Если требуемое имя не будет найдено, интерпретатор выведет сообщение об ошибке.
программирование в shell. Руководство разработчика.
Читайте также
Локальные стили
Локальные стили
То, что у каждого элемента-разделителя есть свое собственное содержание, делает эти элементы прекрасно подходящими для решений на Ajax. И снова HTML5 показывает свое происхождение из спецификации для веб-приложений.Однако если вы попытаетесь перенести
Функции и переменные. Локальные переменные
Функции и переменные. Локальные переменные
Объявленные ранее функции создают внутри своего тела собственные переменные. Это так называемые локальные переменные. Такие переменные доступны только внутри тела функции, в котором они объявлены. При завершении выполнения
4.10 Локальные сети
4.10 Локальные сети
Рассмотрим, как IP и другие протоколы пакетируют кадры для пересылки по локальным сетям. Классическая локальная сеть предполагает следующие свойства:? Станции совместно используют физический носитель.? Существуют правила управления доступом к
Шаг 17 — Как НЕ создавать локальные переменные.
Шаг 17 — Как НЕ создавать локальные переменные.
Что он сделал? Я не постигаю. Что нибудь особенное есть в этих словах: «Буря мглою…»? ___ Повезло ___ стрелял в него этот белогвардеец ___ и тем обеспечил бессмертие.
М. Булгаков. Мастер и Маргарита.
Лирическое отступление номер 2.
Шаг 24 — Как создавать ТОЛЬКО локальные переменные.
Шаг 24 — Как создавать ТОЛЬКО локальные переменные.
В Шаге 17 мы изыскали способ подавить создание локальных переменных. Решим обратную задачу — как подавить иные способы их создания. А какие иные? Любые другие способы предполагают вызов оператора operator new() для выделения
Локальные контексты
Локальные контексты
В совете 35 приведена реализация сравнения строк без учета регистра символов с применением алгоритмов mismatch и lexicographical_compare, но в нем также указано, что полноценное решение должно учитывать локальный контекст. Книга посвящена STL, а не вопросам
Локальные контексты в С++
Локальные контексты в С++
В стандартной библиотеке С++ локальный контекст не является глобальной структурой данных, запрятанной где-то в недрах реализации библиотеки. Это объект типа std::locale, который можно создать и передать его другой функции, как любой другой объект.
5.2.7.1. Локальные переменные DECLARE
5.2.7.1. Локальные переменные DECLARE
DECLARE var_name[,…] type [DEFAULT value]Эта инструкция используется, чтобы объявить локальные переменные. Чтобы обеспечивать значение по умолчанию для переменной, включите предложение DEFAULT. Значение может быть определено как выражение, оно не обязательно
ЛОКАЛЬНЫЕ ПЕРЕМЕННЫЕ
ЛОКАЛЬНЫЕ ПЕРЕМЕННЫЕ
Мы уже несколько раз касались вопроса о том, что переменные в функции являются ее внутренними переменными и «не известны» вызывающей функции. Аналогично переменные вызывающей функции не известны вызываемой функции. Вот почему для связи с ней, т.
5.5.4. Локальные сокеты
5.5.4. Локальные сокеты
Сокеты, соединяющие процессы в пределах одного компьютера, работают в локальном пространстве имен (PF_LOCAL или PF_UNIX, это синонимы). Такие сокеты называются локальными или UNIX-сокетами. Их адресами являются имена файлов, указываемые только при создании
8.3. Локальные объекты
8.3. Локальные объекты
Объявление переменной в локальной области видимости вводит локальный объект. Существует три вида таких объектов: автоматические, регистровые и статические, различающиеся временем жизни и характеристиками занимаемой памяти. Автоматический
22.2. Локальные переменные
22.2. Локальные переменные
Что такое «локальная» переменная?локальные переменныеПеременные, объявленные как локальные, имеют ограниченную область видимости, и доступны только в пределах блока, в котором они были объявлены. Для функций это означает, что локальная
22.2.1. Локальные переменные делают возможной рекурсию.
22.2.1. Локальные переменные делают возможной рекурсию.
Хотя локальные переменные и допускают рекурсию[ 52 ], но она сопряжена с большими накладными расходами и не рекомендуется для использования в сценариях[ 53 ].
14.2. Локальные переменные
14.2. Локальные переменные
Переменные интерпретатора shell могут использоваться сценариями в период функционирования интерпретатора shell. После завершения выполнения интерпретатора действие этих переменных прекращается. Например, локальная переменная имя_файла может
10.4. Локальные сети
10.4. Локальные сети
Локальная сеть создается, как правило, для совместного использования ресурсов ЭВМ или данных (обычно в одной организации). С технической точки зрения локальная сеть – совокупность компьютеров и каналов связи, объединяющих компьютеры в структуру с
Переменные
6. Переменные
Следующий базовый строительный блок, с которым нам нужно ознакомиться, — переменные. Common Lisp поддерживает два вида переменных: лексические и динамические1). Эти два типа переменных примерно соответствуют «локальным» и «глобальным» переменным других языков. Однако, это соответствие лишь приблизительное. С одной стороны «локальные» переменные некоторых языков в действительности гораздо ближе к динамическим переменным Common Lisp 2). И, с другой, локальные переменные некоторых других языков имеют лексическую область видимости не предоставляя всех возможностей, предоставляемых лексическими переменными Common Lisp. В частности, не все языки, предоставляющие переменные, имеющие лексическую область видимости, поддерживают замыкания.
Чтобы сделать все еще более запутанным, многие формы, которые работают с переменными, могут использоваться как с лексическими, так и с динамическими переменными. Поэтому я начну с обсуждения некоторых аспектов переменных Lisp, которые применимы к обоим видам переменных, а затем рассмотрю специфические характеристики лексических и динамических переменных. Далее я обсужу оператор присваивания общего назначения Common Lisp — SETF, который используется для присваивания новых значений переменным, а также – почти любым местам, которые могут хранить значения.
Основы переменных
Как и в других языках, в Common Lisp переменные являются именованными местами, которые могут содержать значения. Однако, в Common Lisp переменные не типизированы таким же образом, как в таких языках, как Java или C++. То есть вам не нужно описывать тип объектов, которые может содержать каждая переменная. Вместо этого, переменная может содержать значения любого типа и сами значения содержат информацию о типе, которая может быть использована для проверки типов во время выполнения. Таким образом, Common Lisp является динамически типизированным: ошибки типов выявляются динамически. Например, если вы передадите не число в функцию +, Common Lisp сообщит вам об ошибке типов. С другой стороны, Common Lisp является строго типизированным языком в том смысле, что все ошибки типов будут обнаружены: нет способа представить объект в качестве экземпляра класса, которым он не является3).
Все значения в Common Lisp, по крайней мере концептуально, являются ссылками на объекты4). Поэтому присваивание переменной нового значения изменяет то, на какой объект ссылается переменная (то есть, куда ссылается переменная), но не оказывает никакого влияния на объект, на который переменная ссылалась ранее. Однако, если переменная содержит ссылку на изменяемый объект, вы можете использовать данную ссылку для изменения этого объекта, и это изменение будет видимо любому коду, который имеет ссылку на этот же объект.
Один из способов введения новой переменной вы уже использовали при определении параметров функции. Как вы видели в предыдущей главе, при определении функции с помощью DEFUN список параметров определяет переменные, которые будут содержать аргументы, переданные функции при вызове. Например, следующая функция определяет три переменные для хранения своих аргументов: x, y и z.
(defun foo (x y z) (+ x y z))
При каждом вызове функции, Lisp создает новые привязки (bindings) для хранения аргументов, переданных при вызове этой функции. Привязка является проявлением переменной во время выполнения. Отдельная переменная — сущность, на которую вы можете сослаться в исходном коде своей программы — может иметь множество различных привязок за время выполнения программы. Отдельная переменная даже может иметь множество привязок в одно и то же время: параметры рекурсивной функции, например, связываются заново (rebound) при каждом вызове этой функции.
Другой формой, позволяющей вводить новые переменные, является специальный оператор LET. Шаблон формы LET имеет следующий вид:
(let (variable*)
body-form*)
где каждая variable является формой инициализации переменной. Каждая форма инициализации является либо списком, содержащим имя переменной и форму начального значения, либо, как сокращение для инициализации переменной в значение NIL, просто именем переменной. Следующая форма LET, например, связывает три переменные x, y и z с начальными значениями 10, 20 и NIL:
(let ((x 10) (y 20) z)
...)
При вычислении формы LET сначала вычисляются все формы начальных значений. Затем, перед выполнением форм тела, создаются и инициализируются в соответствующие начальные значения новые привязки. Внутри тела LET имена переменных ссылаются на только что вновь созданные привязки. После LET имена продолжают ссылаются на то, на что они ссылались перед LET (если они на что-то ссылались).
Значение последнего выражения тела возвращается как значение выражения LET. Как и параметры функций, переменные, вводимые LET, связываются заново (rebound) каждый раз, когда поток управления заходит в LET5).
Область видимости (scope) параметров функций и переменных LET — область программы, где имя переменной может быть использовано для ссылки на привязку переменной — ограничивается формой, которая вводит переменную. Такая форма (определение функции или LET) называется связывающей формой (binding form). Как вы скоро увидите, два типа переменных (лексические и динамические) используют два несколько отличающихся механизма области видимости, но в обоих случаях область видимости ограничена связывающей формой.
Если вы записываете вложенные связывающие формы, которые вводят переменные с одинаковыми именами, то привязки внутренних переменных скрывают внешние привязки. Например, при вызове следующей функции для параметра x создается привязка для хранения аргумента функции. Затем первая LET создает новую привязку с начальным значением 2, а внутренняя LET создает еще одну привязку с начальным значением 3. Комментарии справа указывают область видимости каждой привязки.
(defun foo (x)
(format t "Параметр: ~a~%" x) (let ((x 2)) (format t "Внешний LET: ~a~%" x) (let ((x 3)) (format t "Внутренний LET: ~a~%" x)) (format t "Внешний LET: ~a~%" x)) (format t "Параметр: ~a~%" x)) ; |
Каждое обращение к x будет ссылаться на привязку с наименьшей окружающей областью видимости. Как только поток управления покидает область видимости какой-то связывающей формы, привязка из непосредственно окружающей области видимости перестает скрываться и x ссылается уже на нее. Таким образом, результатом вызова foo будет следующий вывод:
CL-USER> (foo 1)
Параметр: 1
Внешний LET: 2
Внутренний LET: 3
Внешний LET: 2
Параметр: 1
NIL
В последующих главах я обсужу другие конструкции, которые также служат связывающими формами (вообще, любая конструкция, вводящая новые имена переменных, могущие использоваться только внутри этой конструкции, является связывающей формой).
Например, в главе 7 вы встретите цикл DOTIMES, простой цикл-счетчик. Он вводит переменную, которая содержит значение счетчика, увеличивающегося на каждой итерации цикла. Например, следующий цикл, печатающий числа от 0 до 9, связывает переменную x:
(dotimes (x 10) (format t "~d " x))
Ещё одной связывающей формой является вариант LET
: LET*
. Различие состоит в том, что в LET имена переменных могут быть использованы только в теле LET (части LET, идущей после списка переменных), а в LET* формы начальных значений для каждой переменной могут ссылаться на переменные, введенные ранее в списке переменных. Таким образом, вы можете записать следующее:
(let* ((x 10)
(y (+ x 10)))
(list x y))
но не так:
(let ((x 10)
(y (+ x 10)))
(list x y))
Однако, вы можете добиться такого же результата при помощи вложенных LET.
(let ((x 10))
(let ((y (+ x 10)))
(list x y)))
Лексические переменные и замыкания
По умолчанию все связывающие формы в Common Lisp вводят переменные лексической области видимости (lexically scoped). На переменные лексической области видимости можно ссылаться только в коде, который текстуально находится внутри связывающей формы. Лексическая область видимости должна быть знакома каждому, кто программировал на Java, C, Perl или Python, так как все они предоставляют «локальные» переменные, имеющие лексическую область видимости. Программисты на Algol также должны чувствовать себя хорошо, так как этот язык первым ввел лексическую область видимости в 1960-х.
Однако, лексические переменные Common Lisp несколько искажают понятие лексической переменной, по крайней мере в сравнении с оригинальной моделью Algol. Это искажение проявляется при комбинировании лексической области видимости со вложенными функциями. По правилам лексической области видимости, только код, текстуально находящийся внутри связывающей формы, может ссылаться на лексическую переменную. Но что произойдет, когда анонимная функция содержит ссылку на лексическую переменную из окружающей области видимости? Например, в следующем выражении:
(let ((count 0)) #'(lambda () (setf count (+ 1 count))))
ссылка на count внутри формы LAMBDA допустима в соответствии с правилами лексической области видимости. Однако, анонимная функция, содержащая ссылку, будет возвращена как значение формы LET, и она может быть вызвана с помощью FUNCALL кодом, который не находится в области видимости LET. Так что же произойдет? Как выясняется, если count является лексической переменной, все работает. Привязка count, созданная когда поток управления зашел в форму LET, остается столько, сколько это необходимо, в данном случае до тех пор, пока что-то сохраняет ссылку на функциональный объект, возвращенный формой LET. Анонимная функция называется замыканием (closure), потому что она «замыкается вокруг» привязки, созданной LET.
Ключевым моментом для понимания замыканий является то, что захватывается не значение переменной, а привязка. Поэтому замыкание может не только иметь доступ к значению переменной, вокруг которой оно «замкнуто», но и присваивать ей новые значения, которые будут сохраняться между вызовами замыкания. Например, вы можете захватить замыкание, созданное предыдущим выражением, в глобальную переменную следующим образом:
(defparameter *fn* (let ((count 0)) #'(lambda () (setf count (1+ count)))))
Затем, при каждом его вызове значение счетчика будет увеличиваться на единицу.
CL-USER> (funcall *fn*)
1
CL-USER> (funcall *fn*)
2
CL-USER> (funcall *fn*)
3
Отдельное замыкание может «замыкаться вокруг» нескольких привязок переменных просто ссылаясь на них. Также множество замыканий могут захватывать одну и ту же привязку. Например, следующее выражение возвращает список трех замыканий, первое из которых увеличивает значение привязки count, вокруг которой оно «замкнуто», второе – уменьшает его, а третье – возвращает текущее значение:
(let ((count 0))
(list
#'(lambda () (incf count))
#'(lambda () (decf count))
#'(lambda () count)))
Динамические (специальные) переменные
Привязки с лексической областью видимости помогают поддерживать код понятным, путём ограничения области видимости, в которой, буквально говоря, данное имя имеет смысл. Вот почему большинство современных языков программирования используют лексическую область видимости для локальных переменных. Однако, иногда вам действительно может понадобиться глобальная переменная – переменная, к который вы можете обратиться из любой части своей программы. Хотя неразборчивое использование глобальных переменных может привести к «спагетти-коду» также быстро как и неумеренное использование goto, глобальные переменные имеют разумное использование и существуют в том или ином виде почти в каждом языке программирования6). И, как вы сейчас увидите, глобальные переменные Lisp – динамические переменные – одновременно и более удобны, и более гибки.
Common Lisp предоставляет два способа создания глобальных переменных: DEFVAR и DEFPARAMETER. Обе формы принимают имя переменной, начальное значение и опциональную строку документации. После создания переменной с помощью DEFVAR или DEFPARAMETER имя может быть использовано где угодно для ссылки на текущую привязку этой глобальной переменной. Как вы заметили в предыдущих главах, по соглашению, глобальные переменные именуются именами, начинающимися и заканчивающимися *. Далее в этой главе вы увидите, почему очень важно следовать этому соглашению по именованию. Примеры DEFVAR и DEFPARAMETER выглядят следующим образом:
(defvar *count* 0
"Число уже созданных виджетов.")(defparameter *gap-tolerance* 0.001
"Допустимое отклонение интервала между виджетами.")
Различие между этими двумя формами состоит в том, что DEFPARAMETER всегда присваивает начальное значение названной переменной, а DEFVAR делает это только если переменная не определена. Форма DEFVAR также может использоваться без начального значения для определения глобальной переменной без установки ее значения. Такая переменная называется несвязанной (unbound).
На деле, вам следует использовать DEFVAR для определения переменных, которые будут содержать данные, которые вы хотите сохранять даже при изменениях исходного кода, использующего эту переменную. Например, представьте, что две переменные, определенные ранее, являются частью приложения управления «фабрикой виджетов»7). Правильным будет определить переменную *count* с помощью DEFVAR, так как число уже созданных виджетов не становится недействительным лишь потому, что мы сделали некоторые изменения в коде создания виджетов8).
С другой стороны, переменная *gap-tolerance* вероятно влияет некоторым образом на поведение самого кода создания виджетов. Если вы решите, что вам нужно меньшее или большее допустимое отклонение и, следовательно, измените значение в форме DEFPARAMETER, вы захотите, чтобы изменение вступило в силу при перекомпиляции и перезагрузке файла.
После определения переменной с помощью DEFVAR или DEFPARAMETER вы можете ссылаться на нее откуда угодно. Например, вы можете определить следующую функцию для увеличения числа созданных виджетов:
(defun increment-widget-count () (incf *count*))
Преимуществом глобальных переменных является то, что вам не нужно передавать их в функции. Большинство языков программирования хранят потоки стандартного ввода и вывода в глобальных переменных именно по этой причине: вы никогда не знаете, когда именно вы захотите напечатать что-либо на стандартный вывод, и вы не хотите, чтобы каждая функция принимала и передавала далее аргументы, содержащие эти потоки, только потому, что нижележащему коду они могут понадобиться.
Однако, поскольку значение, такое как поток стандартного вывода, хранится в глобальной переменной и вы написали код, ссылающийся на эту глобальную переменную, порой является заманчивым попытаться временно изменить поведение этого кода путем изменения значения переменной.
Например, представьте, что вы работаете над программой, содержащей некоторые низкоуровневые функции журналирования, печатающие в поток, хранящийся в глобальной переменной *standard-output*. Теперь представьте, что в какой-то части программы вы хотите перенаправить весь вывод, генерируемый этими функциями в файл. Вы можете открыть файл и присвоить полученный поток переменной *standard-output*. После этого вышеупомянутые функции будут слать свой вывод в этот файл.
Это работает замечательно, пока вы не забудете восстановить исходное значение *standard-output* после завершения действий. Если вы забудете восстановить *standard-output*, весь остальной код программы, использующий *standard-output*, также будет слать свой вывод в файл9).
Но похоже, что то, что вам действительно нужно, это способ обернуть часть кода во что-то говорящее: «Весь нижележащий код (все функции, которые он вызывает, все функции, которые вызывают эти функции, и так далее до функций самого низкого уровня) должны использовать это значение для глобальной переменной *standard-output*». А затем, по завершении работы функции верхнего уровня, старое значение *standard-output* должно быть автоматически восстановлено.
Оказывается, что это именно те возможности, что предоставляет вам другой вид переменных Common Lisp: динамические переменные. Когда вы связываете динамическую переменную, например с LET-переменной или с параметром функции, привязка, создаваемая во время входа в связывающую форму, заменяет глобальную привязку на все время выполнения связывающей формы. В отличие от лексических привязок, к которым можно обращаться только из кода, находящегося в лексической области видимости связывающей формы, к динамическим привязкам можно обращаться из любого кода, вызываемого во время выполнения связывающей формы10). И оказывается, что все глобальные переменные на самом деле являются динамическими.
Таким образом, если вы хотите временно переопределить *standard-output*, это можно сделать просто пересвязав ее, например, с помощью LET.
(let ((*standard-output* *some-other-stream*))
(stuff))
В любом коде, который выполняется в результате вызова stuff, ссылки на *standard-output* будут использовать привязку, установленную с помощью LET. А после того как stuff завершится и поток управления покинет LET, новая привязка *standard-output* исчезнет и последующие обращения к *standard-output* будут видеть привязку, бывшую до LET. В любой момент времени самая последняя установленная привязка скрывает все остальные. Можно представить, что каждая новая привязка данной динамической переменной помещается в стек привязок этой переменной и ссылки на эту переменную всегда используют последнюю установленную привязку. После выхода из связывающей формы созданные в ней привязки убираются из стека, делая видимыми предыдующие привязки11).
Простой пример показывает как это работает:
(defvar *x* 10)
(defun foo () (format t "X: ~d~%" *x*))
DEFVAR создает глобальную привязку переменной *x* со значением 10. Обращение к *x* в foo будет искать текущую привязку динамически. Если вы вызовете foo на верхнем уровне (from the top level), глобальная привязка, созданная DEFVAR, будет единственной доступной привязкой, поэтому будет напечатано 10.
CL-USER> (foo)
X: 10
NIL
Но вы можете использовать LET для создания новой привязки, которая временно скроет глобальную привязку, и foo напечатает другое значение.
CL-USER> (let ((*x* 20)) (foo))
X: 20
NIL
Теперь снова вызовем foo без LET, она опять будет видеть глобальную привязку.
CL-USER> (foo)
X: 10
NIL
Теперь определим новую функцию.
(defun bar ()
(foo)
(let ((*x* 20)) (foo))
(foo))
Обратите внимание, что средний вызов foo находится внутри LET, которая связывает *x* с новым значением 20. При вызове bar вы получите следующий результат:
CL-USER> (bar)
X: 10
X: 20
X: 10
NIL
Как вы можете заметить, первый вызов foo видит глобальную привязку со значением 10. Средний вызов видит новую привязку со значением 20. А после LET, foo снова видит глобальную привязку.
Как и с лексической привязкой, присваивание нового значения влияет только на текущую привязку. Чтобы увидеть это, вы можете переопределить foo, добавив присваивание значения переменной *x*.
(defun foo ()
(format t "Перед присваиванием~18tX: ~d~%" *x*)
(setf *x* (+ 1 *x*))
(format t "После присваивания~18tX: ~d~%" *x*))
Теперь foo печатает значение *x*, увеличивает его на единицу, а затем печатает его снова. Если вы просто запустите foo, вы увидите следующее:
CL-USER> (foo)
Перед присваиванием X: 10
После присваивания X: 11
NIL
Ничего удивительного. Теперь запустим bar.
CL-USER> (bar)
Перед присваиванием X: 11
После присваивания X: 12
Перед присваиванием X: 20
После присваивания X: 21
Перед присваиванием X: 12
После присваивания X: 13
NIL
Обратите внимание, начальное значение *x* равно 11: предыдущий вызов foo действительно изменил глобальное значение. Первый вызов foo из bar увеличивает глобальную привязку до 12. Средний вызов не видит глобальную привязку из-за LET. А затем последний вызов снова может видеть глобальную привязку и увеличивает ее с 12 до 13.
Так как это работает? Как LET знает, когда связывает *x*, что подразумевается создание динамической привязки вместо обычной лексической? Она знает, так как имя было объявлено специальным12). Имя каждой переменной, определенной с помощью DEFVAR и DEFPARAMETER автоматически глобально объявляется специальным. Это означает, что когда бы вы не использовали это имя в связывающей форме (в форме LET, или как параметр функции, или в любой другой конструкции, которая создает новую привязку переменной, вновь создаваемая привязка будет динамической. Вот почему *соглашение* *по* *именованию* так важно: будет не очень хорошо, если вы используете имя, о котором вы думаете как о лексической переменной, а эта переменная окажется глобальной специальной. С одной стороны, код, который вы вызываете, сможет изменить значение этой связи; с другой, вы сами можете скрыть связь, установленную кодом, находящимся выше по стеку. Если вы всегда будете именовать глобальные переменные, используя соглашение по именованию *, вы никогда случайно не воспользуетесь динамической связью, желая создать лексическую.
Также возможно локально объявить имя специальным. Если в связывающей форме вы объявите имя специальным, привязка, созданная для этой переменной, будет динамической, а не лексической. Другой код может локально определить имя специальным, чтобы обращаться к динамической привязке. Однако, локальные специальные переменные используются относительно редко, поэтому вам не стоит беспокоиться о них13).
Динамические привязки делают глобальные переменные гораздо более гибкими, но важно понимать, что они позволяют осуществлять незаметные действия на расстоянии. Связывание глобальной переменной имеет два дальнодействующих эффекта: оно может изменить поведение нижележащего кода, а также открывает нижележащему коду возможность присваивания нового значения привязке, установленной выше по стеку. Вы должны использовать динамические переменные только в том случае, если вам нужно получить преимущества от одного или обоих из этих эффектов.
Константы
Еще одним видом переменных, вообще не упомянутых ранее, являются оксюморонические «константные переменные». Все константы являются глобальными и определяются с помощью DEFCONSTANT. Базовая форма DEFCONSTANT подобна DEFPARAMETER.
(defconstant name initial-value-form [ documentation-string ])
Как и в случае с DEFPARAMETER, DEFCONSTANT оказывает глобальный эффект на используемое имя: после этого, имя может быть использовано только для обращения к константе; оно не может быть использовано как параметр функции или быть пересвязано с помощью любой другой связывающей формы. Поэтому многие программисты на Lisp следуют соглашению по именованию и используют для констант имена начинающиеся и заканчивающиеся знаком +. Этому соглашению следуют немного в меньшей степени, чем соглашению для глобальных динамических имен, но оно является хорошей идеей по сходным причинам14).
Ещё один важный момент: несмотря на то что язык позволяет вам переопределять константы путем перевычисления DEFCONSTANT с другой формой начального значения, не определено то, что именно произойдет после такого переопределения. На практике, большинство реализаций требуют, чтобы вы перевычислили любой код, ссылающийся на константу, чтобы изменение вступило в силу, так как старое значение могло быть встроено (inlined). Следовательно, правильным будет использовать DEFCONSTANT для определения только тех вещей, которые действительно являются константами, такие как значение NIL. Для вещей, которые вам может когда-нибудь понадобиться изменить, следует использовать DEFPARAMETER.
Присваивание
После создания привязки вы можете совершать с ней два действия: получить текущее значение и установить ей новое значение. Как вы видели в главе 4, символ вычисляется в значение переменной, которую он именует, поэтому вы можете получить текущее значение просто обратившись к переменной. Для присваивания нового значения привязке используйте макрос SETF, являющийся в Common Lisp оператором присваивания общего назначения. Базовая форма SETF следующая:
(setf place value)
Так как SETF является макросом, он может оценить форму «места», которому он осуществляет присваивание и расшириться (expand) в соответствующие низкоуровневые операции, осуществляющие необходимые действия. Когда «место» является переменной, этот макрос расширяется в вызов специального оператора SETQ, который, как специальный оператор, имеет доступ и к лексическим, и к динамическим привязкам15). Например, для присваивания значения 10 переменной x вы можете написать это:
(setf x 10)
Как я рассказал ранее, присваивание нового значения привязке не оказывает никакого влияния на остальные привязки этой переменной. И оно не оказывает никакого влияния на значение, которое хранилось в привязке до присваивания. Таким образом, SETF в следующей функции:
(defun foo (x) (setf x 10))
не окажет никакого влияния на любое значение вне foo. Привязка, которая создается при вызове foo, устанавливается в 10, незамедлительно заменяя то значение, что было передано в качестве аргумента. В частности, следующая форма:
(let ((y 20))
(foo y)
(print y))
напечатает 20, а не 10, так как именно оно является значением y
, которое передается foo
, где уже является значением переменной x
перед тем, как SETF дает x
новое значение.
SETF также может осуществить последовательное присваивание множеству «мест». Например, вместо следующего:
(setf x 1)
(setf y 2)
вы можете записать следующее:
(setf x 1 y 2)
SETF возвращает присвоенное значение, поэтому вы можете вкладывать вызовы SETF как в следующем примере, который присваивает и x, и y одинаковое случайное значение:
(setf x (setf y (random 10)))
Обобщенное присваивание
Привязки переменных, конечно, не являются единственными «местами», которые могут содержать значения. Common Lisp поддерживает составные структуры данных, такие как массивы, хэш-таблицы, списки, а также определенные пользователем структуры данных — такие структуры состоят из множества «мест», способных содержать значения.
Я опишу эти структуры данных в последующих главах, но так как мы рассматриваем присваивание, вы должны знать, что SETF может присвоить значение любому «месту». Когда я буду описывать различные составные структуры данных, я буду указывать, какие функции могут использоваться как «места, обрабатываемые SETF» («SETFable places»). Кратко же можно сказать, что если вам нужно присвоить значение «месту», почти наверняка следует использовать SETF. Возможно даже расширить SETF для того, чтобы он мог осуществлять присваивание определенным пользователем «местам», хотя я не описываю такие возможности16).
В этом отношении SETF не отличается от оператора присваивания = языков, произошедших от C. В этих языках оператор = присваивает новые значения переменным, элементам массивов, полям классов. В языках, таких как Perl и Python, которые поддерживают хэш-таблицы как встроенные типы данных, = может также устанавливать значения элементов хэш-таблицы. Таблица 6-1 резюмирует различные способы, которыми используется = в этих языках.
Таблица 6-1. Присваивание с помощью = в других языках программирования
Присваивание … | Java, C, C++ | Perl | Python |
---|---|---|---|
… переменной | x = 10; | $x = 10; | x = 10 |
… элементу массива | a[0] = 10; | $a[0] = 10; | a[0] = 10 |
… элементу хэш-таблицы | – | $hash{‘key’} = 10; | hash[‘key’] = 10 |
… полю объекта | o.field = 10; | $o->{‘field’} = 10; | o.field = 10 |
SETF работает сходным образом: первый «аргумент» SETF является «местом» для хранения значения, а второй предоставляет само значения. Как и с оператором = в этих языках, вы используете одинаковую форму и для выражения «места», и для получения значения17). Таким образом, эквиваленты вышеприведенных в таблице 6-1 присваиваний для Lisp следующие (AREF — функция доступа к массиву, GETHASH осуществляет операцию поиска в хэш-таблице, а field может быть функцией, которая обращается к слоту под именем field определенного пользователем объекта):
Простая переменная: (setf x 10)
Массив: (setf (aref a 0) 10)
Хэш-таблица: (setf (gethash 'key hash) 10)
Слот с именем 'field': (setf (field o) 10)
Обратите внимание, что присваиваение с помощью SETF «месту», которое является частью большего объекта, имеет ту же семантику, что и присваивание переменной: «место» модифицируется без оказания какого-либо влияния на объект, который хранился там до этого. И вновь, это подобно тому, как ведет себя = в Java, Perl и Python18).
Другие способы изменения «мест»
В то время как все присваивания можно выразить с помощью SETF, некоторые образцы, включающие присваивания нового значения, основанного на текущем значении, являются достаточно общими для того, чтобы получить свои собственные операторы. Например, вы можете увеличить число с помощью SETF следующим образом:
(setf x (+ x 1))
или уменьшить его так:
(setf x (- x 1))
Но это слегка утомительно по сравнению с стилем C: ++x и
—
x. Вместо этого вы можете использовать макросы INCF и DECF, которые увеличивают и уменьшают «место» на определенную величину, по умолчанию 1.
(incf x) === (setf x (+ x 1))
(decf x) === (setf x (- x 1))
(incf x 10) === (setf x (+ x 10))
INCF и DECF являются примерами определенного вида макросов, называемых модифицирующими макросами (modify macros). Модифицирующие макросы являются макросами, построенными поверх SETF, которые модифицируют «места» путем присваивания нового значения, основанного на их текущем значении. Главным преимуществом таких макросов является то, что они более краткие, чем аналогичные операции, записанные с помощью SETF. Вдобавок, модифицирующие макросы определены таким образом, чтобы быть безопасными при использовании с «местами», когда выражение «места» должно быть вычислено лишь единожды. Несколько надуманным примером является следующее выражение, которое увеличивает значение произвольного элемента массива:
(incf (aref *array* (random (length *array*))))
Наивный перевод этого примера в выражение, использующее SETF, может выглядить следующим образом:
(setf (aref *array* (random (length *array*)))
(1+ (aref *array* (random (length *array*)))))
Однако, это не работает, так как два последовательных вызова RANDOM не обязательно вернут одинаковое значение: это выражение вероятно получит значение одного элемента массива, увеличит его, а затем сохранит его как новое значение другого элемента массива. Однако, выражение INCF сделает все правильно, так как знает, как правильно разобрать это выражение:
(aref *array* (random (length *array*)))
чтобы извлечь те части, которые возможно могут иметь побочные эффекты, и гарантировать, что они будут вычисляться лишь один раз. В этом случае, выражение INCF вероятно расширится в нечто более или менее подобное этому:
(let ((tmp (random (length *array*))))
(setf (aref *array* tmp) (1+ (aref *array* tmp))))
Вообще, модифицирующие макросы гарантируют однократное вычисление слева направо своих аргументов, а также подформ формы места (place form).
Макрос PUSH, который вы использовали в примере с базой данных для добавления элементов в переменную *db*, является еще одним модифицирующим макросом. Более подробно о его работе и работе POP и PUSHNEW будет сказано в главе 12, где я буду говорить о том, как представляются (represented) списки в Lisp.
И наконец, два слегка эзотерических, но полезных модифицирующих макроса – ROTATEF и SHIFTF. ROTATEF циклически сдвигает значение между «местами». Например, если вы имеете две переменные, a и b, этот вызов:
(rotatef a b)
обменяет значения двух переменных и вернет NIL. Так как a и b являются переменными и вам не нужно беспокоиться о побочных эффектах, предыдущее выражение ROTATEF эквивалентно следующему:
(let ((tmp a)) (setf a b b tmp) nil)
С другими видами «мест» эквивалентное выражение с использованием SETF может быть более сложным.
SHIFTF подобен ROTATEF за исключением того, что вместо циклического сдвига значений, он просто сдвигает их влево: последний аргумент предоставляет значение, которое перемещается в предпоследний аргумент и так далее. Исходное значение первого аргумента просто возвращается. Таким образом, следующее:
(shiftf a b 10)
эквивалентно (и снова, так как вам не нужно беспокоиться о побочных эффектах) следующему:
(let ((tmp a)) (setf a b b 10) tmp)
И ROTATEF, и SHIFTF могут использоваться с любым числом аргументов, и, как все модифицирующие макросы, гарантируют однократное их вычисление слева направо.
С базовыми знаниями функций и переменных Common Lisp в своем арсенале вы готовы перейти к следующей особенности, которая еще больше отличает Lisp от других языков программирования: макросы.
Локальная переменная
Локальная переменная
Элемент
диаграммы действий.
При задании алгоритма с помощью диаграммы действий вам может понадобиться объявить какую-то локальную переменную, например, для хранения промежуточных результатов вычислений. Локальную переменную можно задать с помощью одноименного блока диаграммы
действий.
Пожалуйста, обратите внимание, что локальная переменная будет видна не во всей диаграмме действий, а только в той ее части, которая следует за точкой объявления переменной.
Чтобы объявить локальную переменную внутри диаграммы действия
-
Перетащите элемент
Локальная переменная
из палитры
Диаграмма действий
на диаграмму типа агентов. Перетаскивая блок, отпустите кнопку мыши тогда, когда курсор мыши будет находиться над точкой, которая находится в той ветви диаграммы действий, куда вы хотите поместить данный блок (при наведении курсора эта точка должна
подсветиться зеленым цветом). - В выбранной вами ветви диаграммы действий должен появиться новый блок.
- Перейдите в панель
Свойства. - Введите имя этого блока (и в то же время имя локальной переменной, задаваемой этим блоком) в поле
Имя. Имя переменной будет отображено на диаграмме внутри данного блока. - Задайте тип переменной. Вы можете как выбрать один из наиболее часто используемых типов (int,
double,
boolean,
String) с помощью соответствующей кнопки переключателя
Тип, так и задать значение любого другого Java класса (в этом случае вам нужно будет выбрать опцию
Другой
и ввести имя типа в расположенном справа поле). - Введите начальное значение переменной в поле
Начальное значение. - Некоторые переменные могут не изменять своих значений, а сохранять одно и то же значение на протяжении всего процесса моделирования. Такие переменные должны быть объявлены константами.
Константа
сохраняет одно и то же значение, которое не может быть изменено пользователем. Сделав переменную константой, вы защищаете ее от нежелательного изменения. Чтобы сделать переменную константой, установите флажок
Константа.
Свойства
-
Основные -
Имя
– Имя локальной переменной, заданной данным блоком.
Тип
– Тип локальной переменной. Вы можете как выбрать один из наиболее часто используемых типов (int,
double,
boolean,
String) с помощью соответствующей кнопки переключателя
Тип, так и задать значение любого другого Java класса (в этом случае вам нужно будет выбрать опцию
Другой
и ввести имя типа в расположенном справа поле).
Начальное значение
– Здесь вы можете задать начальное значение переменной. Начальное значение может быть впоследствии изменено во время моделирования. Если начальное значение не будет задано, то будут применены правила Java: например, переменная типа
double
будет равна 0, а переменной типа
boolean
будет присвоено значение
false.
Константа
– Если опция выбрана, то локальная переменная будет константой, то есть, она всегда будет сохранять одно и то же значение. -
Специфические -
Метка
– Здесь вы можете ввести комментарий, объясняющий смысл переменной, заданной этим блоком. Комментарий будет отображен внутри блока вместо имени переменной.
Цвет заливки
— Задает цвет заливки блока. Щелкните мышью внутри элемента управления и выберите нужный цвет из списка наиболее часто используемых цветов или же выберите любой другой цвет с помощью диалога
Цвета.
См. также
Визуальное задание алгоритмов с помощью диаграмм действий
Редактирование блоков диаграммы действий
Глобальные переменные (глобальные), локальные переменные (локальные), нелокальные переменные в Python
В этом уроке Python вы узнаете о глобальных переменных, локальных переменных, нелокальных переменных в Python и использовании этих переменных.
Глобальные переменные в Python
В языке программирования Python переменная, объявленная вне функции или в глобальной области видимости, называется глобальной переменной или глобальной переменной. Доступ к глобальным переменным можно получить как изнутри, так и вне функции.
Взгляните на пример того, как создавать глобальные переменные в Python.
x = “Глобальная переменная” # Объявляет переменную x
# Вызов x из функции vidu ()
def view ():
print (“x в функции vidu ():”, x) видеть ()
# Вызов x вне функции vidu ()
print (“x вне функции vidu ():”, x)
В приведенном выше примере мы объявляем переменную x как глобальную переменную и определяем функцию vidu () для печати переменной x. Наконец, мы вызываем vidu () для вывода значения переменной x. Выполнение приведенного выше кода приведет к:
x в vidu (): глобальная переменная
x за пределами vidu (): глобальная переменная
Что произойдет, если вы измените значение x в функции?
х = 2
def view ():
х = х * 2
печать (х) видеть ()
Если вы запустите этот код, вы получите сообщение об ошибке:
No connectionLocalError: xx внутренний адрес, указанный перед преобразованием
Эта ошибка возникает из-за того, что Python рассматривает x как локальную переменную, а x не определен в vidu ().
Чтобы изменить глобальные переменные в функции, вам нужно будет использовать ключевое слово global. Подробнее о глобальных ключевых словах мы поговорим в статье.
Локальная переменная в Python
Переменные, объявленные внутри функции или в локальной области видимости, называются локальными или локальными переменными.
def view ():
y = “Локальная переменная”
видеть ()
печать (и)
Когда вы запустите приведенный выше код, вы получите сообщение об ошибке:
NameError: имя ‘y’ не определено
Эта ошибка возникает из-за того, что мы пытались получить доступ к локальной переменной y в глобальной области видимости, но она работает только в функции vidu () или локальной области.
Обычно, чтобы создать локальную переменную, мы объявляем ее в функции, как в примере ниже:
def view ():
y = “Локальная переменная”
печать (и)
видеть ()
Выполнение приведенного выше кода приведет к
Локальная переменная
Мы возвращаемся к рассмотрению предыдущей проблемы, где x – глобальная переменная, и мы хотим изменить x в vidu ().
Локальные переменные и глобальные переменные
Здесь мы узнаем, как использовать локальные и глобальные переменные в одном коде.
х = 2 def view ():
глобальный x
y = “Локальная переменная”
х = х * 2
печать (х)
печать (и)
# Автор: TipsMake.com
видеть ()
Запустив приведенный выше код, мы получим результат:
4
Локальная переменная
В приведенном выше коде мы объявляем x как глобальную переменную, а y – как локальную переменную в vidu (), и используем оператор * для изменения глобальных переменных и печати обоих значений x и y. После вызова vidu () значение x будет 4, потому что оно дублируется.
Примеры использования глобальных переменных и локализованных имен:
х = 5 def view ():
х = 10
print (“Локальная переменная:”, x)
видеть ()
print (“Глобальная переменная x:”, x)
После выполнения приведенного выше кода у нас есть результат:
Локальная переменная: 10
Глобальная переменная x: 5
В приведенном выше коде мы используем одно и то же имя x как для локальных, так и для глобальных переменных. При печати одной и той же переменной x мы получаем два разных результата, потому что переменная объявлена как в области видимости, так и в локальной (внутри функции vidu ()) и глобальной (вне функции vidu ()).
Когда мы печатаем переменную в функции vidu (), она выводит Biến x cục bộ: 10, это называется локальной областью видимости переменной. Точно так же, когда мы печатаем переменные вне функции, vidu () будет производить Biến x toàn cục: 5, это глобальная область видимости переменной.
Нелокальная переменная в Python
Из этого нелокального я не знаю, как его перевести на вьетнамский язык, так что он стандартный. В Python нелокальная переменная используется во вложенных функциях, где локальная область видимости не определена. Понятно, что нелокальная переменная не является локальной переменной, не глобальной переменной, вы объявляете переменную нелокальной, когда хотите использовать ее в более широком диапазоне, чем локальный, но не на глобальном уровне.
Чтобы объявить переменную нелокальной, нам нужно использовать ключевое слово нелокальной.
Например:
def ham_ngoai ():
x = “Локальная переменная” def ham_trong ():
нелокальный x
x = “Нелокальная переменная”
print (“Внутри:”, x)
ham_trong ()
print (“Внешний:”, x)
Hamngoai ()
Запустив приведенный выше код, вы получите результат:
Внутри: переменная нелокальная
Снаружи: переменная нелокальная
В приведенном выше коде есть вложенная функция ham_trong (), мы используем ключевое слово nonlocal для создания нелокальной переменной. Функция ham_trong () определена в области видимости ham_ngoai ().
Примечание. Если мы изменим значение нелокальной переменной, изменение отобразится в локальной переменной.
Не забывайте делать домашнее задание по Python.
Следующая статья: Глобальное ключевое слово в Python
Предыдущая статья: Анонимная функция, Лямбда в Python
Локальные и глобальные переменные Python
Локальные и глобальные переменные Python — мы можем определить область действия переменной как степень программы, в которой мы можем получить доступ к переменной. В этом уроке мы рассмотрим локальные переменная в Python и глобальная переменная в Python. Мы также обсудим область действия локальной и глобальной переменной в Python и то, как использовать глобальные переменные внутри локальной области.
In Питон, есть две основные области действия переменных:
- Локальная переменная в Python
- Глобальная переменная в Python
Глобальная переменная в Python
Глобальная переменная в Python — переменные, созданные вне функция называются глобальными переменными. Кто угодно может получить доступ к глобальной переменной как внутри, так и вне функции. Ниже программа для глобальной переменной в Python.
#variable defined in global scope var='Hello world' #function accessing the global variable def my_function(): print(var) #calling the function my_function()
Здесь мы создаем переменную с именем «var» и присваиваем ей значение «Hello world». Мы определяем переменную var вне функции my_function. Когда мы вызываем функцию, она печатает значение переменной ‘var’. Поскольку у нас нет другой переменной с тем же именем, определенной где-либо внутри функции, глобальная переменная печатается.
Локальная переменная в Python
Переменная, определенная внутри функции, называется локальной переменной. Мы можем получить доступ к локальной переменной только внутри функции. Ниже представлена программа для локальной переменной в Python.
def my_function(): #variable defined in local scope var='Hello world' print(var) #calling the function my_function()
Что происходит, когда мы пытаемся Распечатать переменная вне функции?
>>> var Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'var' is not defined >>>
Когда мы пытаемся получить доступ к локальной переменной вне функции, возникает ошибка.
Объем локальных и глобальных переменных
Допустим, мы определяем переменную с тем же именем как внутри, так и вне функции. Затем внутри функции переменной, определенной в локальной области видимости, дается приоритет над переменной, определенной в глобальной области видимости.
var='global scope' def my_function(): var='local scope' print(var) my_function() print(var)
Выходной сигнал будет:
local scope global scope
Использование глобальных переменных внутри локальной области видимости
Несмотря на то, что мы определяем переменную с тем же именем внутри и вне функции, изменение значения переменной в локальной области не влияет на значение переменной в глобальной области. Что делать, если вы хотите изменить значение глобальной переменной внутри функции.
Внутри функции мы должны использовать ключевое слово global перед переменной, чтобы мы могли изменить переменную на глобальном уровне.
var='global scope' def my_function(): global var print(var) var='local scope' my_function() print(var)
И результат:
global scope local scope
Внутри функции мы упоминаем, что переменная является глобальной переменной. Мы изменяем значение переменной внутри функции, и когда мы печатаем переменную вне функции, она отображает измененное значение. Вот как мы меняем значение глобальной переменной внутри функции.
Приложение
В официальной документации вы можете найти дополнительную информацию о правила для локальных и глобальных переменных.
Заключение
Мы подробно рассмотрели локальные и глобальные переменные Python. Мы также обсудили область действия локальных и глобальных переменных и то, как использовать глобальные переменные внутри локальной области.
|
|
Область видимости переменных в Python
Как правило, переменная, определенная в блоке, доступна только в этом блоке. Он недоступен за пределами квартала. Такая переменная называется локальной переменной. Формальные идентификаторы аргументов также действуют как локальные переменные.
Следующий пример подчеркивает этот момент. Попытка распечатать локальную переменную вне ее области действия вызовет исключение NameError
.
def greet ():
name = 'Стив'
print ('Привет', имя)
Здесь name
— это локальная переменная для функции greet ()
, недоступная вне ее.
>>> привет ()
Привет Стив
>>> имя
Отслеживание (последний вызов последний):
Файл "", строка 1, в имени
NameError: имя 'name' не определено
Любая переменная, присутствующая вне любого функционального блока, называется глобальной переменной.Его значение доступно изнутри любой функции. В следующем примере переменная name
инициализируется перед определением функции.
Следовательно, это глобальная переменная.
name = 'Джон'
def greet ():
print ("Привет", имя)
Теперь вы можете получить доступ к глобальной переменной с именем
, потому что она была определена вне функции.
>>> привет ()
Привет Стив
>>> имя
'Стив'
Однако, если мы присвоим другое значение глобально объявленной переменной внутри функции, в пространстве имен функции будет создана новая локальная переменная.
Это присвоение не изменит значение глобальной переменной. Например:
name = 'Стив'
def greet ():
name = 'Bill'
print ('Привет', имя)
Теперь изменение значения глобальной переменной name
внутри функции не повлияет на ее глобальное значение.
>>> привет ()
Привет Билл
>>> имя
'Стив'
Если вам нужно получить доступ и изменить значение глобальной переменной из функции, это разрешение предоставляется глобальным ключевым словом
.
name = 'Стив'
def greet ():
глобальное имя
name = 'Bill'
print ('Привет', имя)
Приведенное выше будет отображать следующий вывод в оболочке Python.
>>> имя
'Стив'
>>> привет ()
Привет Билл
>>> имя
'Билл'
Также возможно одновременное использование глобальной и локальной переменных с одним и тем же именем. Встроенная функция globals ()
возвращает объект словаря всех глобальных переменных и их соответствующих значений.Используя имя переменной в качестве ключа, можно получить доступ к ее значению и изменить его.
name = 'Стив'
def greet ():
globals () ['name'] = 'Джеймс'
name = 'Стив'
print ('Привет', имя)
Результат приведенного выше кода показывает конфликт между глобальными и локальными переменными с одинаковым именем и способ его разрешения.
>>> имя
'Стив'
>>> привет ()
Привет Стив
>>> имя
'Джеймс'
Посетите Globals and Locals in Python для получения дополнительной информации.
Область видимости переменных Python с локальными и нелокальными примерами
Если вы знакомы с Python или любым другим языком программирования, вы, несомненно, знаете, что переменные необходимо определить, прежде чем их можно будет использовать в вашей программе.В этом руководстве вы начнете с инициализации переменной. Далее вы познакомитесь с границей переменных в программе — ее «областью действия». Вы узнаете о четырех различных областях видимости на примерах: локальная, включающая, глобальная и встроенная. Эти области вместе составляют основу правила LEGB, используемого интерпретатором Python при работе с переменными. Затем вы еще раз вернетесь к некоторым примерам с дополнительной сложностью, чтобы открыть путь для ключевого слова global, за которым следует ключевое слово nonlocal.
Переменная
Переменная — это метка или имя, присвоенное определенной области памяти. Это место содержит значение, которое вы хотите запомнить в вашей программе для использования в дальнейшем. В Python замечательно то, что вам не нужно явно указывать тип переменной, которую вы хотите определить — он может быть любого типа (строка, целое число, число с плавающей запятой и т. Д.). Чтобы создать новую переменную в Python, вы просто используете оператор присваивания ( =
, единственный знак равенства) и присваиваете ему желаемое значение.
first_string_var = "Первая строка"
first_int_var = 1
итого = 1 + 2 * 3
Присвоение начального значения переменной называется инициализацией переменной. Вы только что инициализировали переменную: first_string_var
строковым значением Первая строка и переменная first_int_var
целочисленным или числовым значением 1 .
Часть слева от оператора присваивания — это имя переменной, а правая часть — ее значение.Правая часть также может быть арифметической операцией — в этом случае она будет вычислена до того, как произойдет присваивание.
В Python есть несколько правил, которым вы должны следовать при создании переменной …
- Он может содержать только буквы (в верхнем или нижнем регистре), цифры или символ подчеркивания
_
. - Он не может начинаться с числа.
- Это может быть не ключевое слово (вы узнаете о них позже).
Если вы не соблюдаете эти правила, вы получите сообщение об ошибке.SyntaxError: невозможно присвоить ключевому слову
Диапазон изменения
Теперь, когда вы знаете, как инициализировать переменную. Поговорим об объеме этих переменных. Не все переменные доступны из любого места в программе. Часть программы, в которой доступна переменная, называется ее областью действия. Существует четыре основных типа области видимости переменных, которые лежат в основе правила LEGB . LEGB означает Local -> Enclosing -> Global -> Built-in .
Давайте узнаем больше о прицелах…
Локальная область действия
Всякий раз, когда вы определяете переменную внутри функции, ее область действия находится ТОЛЬКО внутри функции. Он доступен с точки, в которой он определен, до конца функции и существует до тех пор, пока функция выполняется (Источник). Это означает, что его значение не может быть изменено или даже доступно извне функции. Возьмем простой пример:
def print_number ():
first_num = 1
# Распечатать оператор 1
print ("Первое определенное число:", first_num)
print_number ()
# Распечатать оператор 2
print ("Первое определенное число:", first_num)
Первое определенное число: 1
-------------------------------------------------- -------------------------
NameError Traceback (последний вызов последним)
в
6 print_number ()
7 # Распечатать оператор 2
----> 8 print ("Первое определенное число:", first_num)
NameError: имя 'first_num' не определено
Мы смогли напечатать переменную first_num
, вызвав функцию print_number ()
(# Print statement 1).Но при попытке доступа и последующего вывода той же переменной извне функции (оператор # Print 2) возникла ошибка NameError
. Это связано с тем, что first_num
является «локальным» для функции, поэтому к нему нельзя получить доступ извне тела функции.
Объем
Что делать, если у нас есть вложенная функция (функция, определенная внутри другой функции)? Как меняется прицел? Посмотрим на примере.
def external ():
first_num = 1
def inner ():
second_num = 2
# Распечатать оператор 1 - Объем: Внутренний
print ("первое_число из внешнего:", первое_число)
# Распечатать оператор 2 - Объем: Внутренний
print ("второе_число из внутреннего:", второе_число)
внутренний()
# Распечатать оператор 3 - Объем: Внешний
print ("второе_число из внутреннего:", второе_число)
внешний ()
first_num от внешнего: 1
second_num из внутреннего: 2
-------------------------------------------------- -------------------------
NameError Traceback (последний вызов последним)
в
11 print ("второе_число из внутреннего:", второе_число)
12
---> 13 внешний ()
в external ()
9 внутренний ()
10 # Распечатать оператор 3 - Объем: Внешний
---> 11 print ("второе_число из внутреннего:", второе_число)
12
13 внешний ()
NameError: имя 'second_num' не определено
Ошибка? Это потому, что вы не можете получить доступ к second_num
из external ()
(# Print statement 3).Это не определено в этой функции. Однако вы можете получить доступ к first_num
из inner ()
(# Print statement 1), поскольку область first_num
больше, она находится в пределах external ()
.
Это область применения . Внешние переменные
имеют больший объем и могут быть доступны из вложенной функции inner ()
.
Глобальный охват
Это, пожалуй, самый простой для понимания прицел. Всякий раз, когда переменная определяется вне какой-либо функции, она становится глобальной переменной, а ее область действия находится в любом месте программы.Это означает, что его можно использовать в любой функции.
welcome = "Привет"
def welcome_world ():
world = "Мир"
печать (приветствие, мир)
def welcome_name (имя):
печать (приветствие, имя)
приветствие_world ()
welcome_name ("Самуил")
Привет, мир
Привет, Самуэль
Встроенный осциллограф
Это самый широкий диапазон из существующих! Все специальные зарезервированные ключевые слова подпадают под эту область.
Мы можем вызывать ключевые слова в любом месте нашей программы, не определяя их перед использованием.
Ключевые слова — это просто специальные зарезервированные слова. Они хранятся для определенных целей и не могут использоваться для каких-либо других целей в программе.
Это ключевые слова в Python:
Правило LEGB
LEGB (Local -> Enclosing -> Global -> Built-in) — это логика, которой следует интерпретатор Python при выполнении вашей программы.
Допустим, вы вызываете print (x)
внутри inner ()
, который является функцией, вложенной в external ()
.Затем Python сначала проверяет, определено ли «x» локально в пределах inner ()
. В противном случае будет использоваться переменная, определенная в external ()
. Это закрывающая функция. Если он также не был определен там, интерпретатор Python перейдет на другой уровень — в глобальную область видимости. Выше этого вы найдете только встроенную область видимости, которая содержит специальные переменные, зарезервированные для самого Python.
Пока все хорошо!
Затем давайте вернемся к некоторым примерам из прошлого, чтобы увидеть, могут ли они создавать проблемы, когда вариант использования становится немного более сложным.
Сценарий 1: Глобальный охват
Помните функцию welcome_world ()
, описанную ранее? Допустим, вы хотели изменить глобальную переменную welcome
(«Hello»), чтобы установить новое значение («Hi») для приветствия, чтобы welcome_world ()
выводил «Hi World»
welcome = "Привет"
def change_greeting (new_greeting):
приветствие = new_greeting
def welcome_world ():
world = "Мир"
печать (приветствие, мир)
change_greeting («Привет»)
приветствие_world ()
Привет, мир
Хорошо…то пошло не так, как хотелось! Почему?
Это связано с тем, что, когда мы устанавливаем значение приветствия
на «Привет», оно создает новую локальную переменную приветствия
в области действия change_greeting ()
. Это ничего не изменило для глобального приветствия
. Здесь пригодится глобальное ключевое слово .
Глобальное ключевое слово
С global вы говорите Python использовать глобально определенную переменную вместо того, чтобы создавать ее локально.Чтобы использовать ключевое слово, просто введите global, а затем имя переменной. Давайте посмотрим, как это работает в Сценарии 1.
welcome = "Привет"
def change_greeting (new_greeting):
глобальное приветствие
приветствие = new_greeting
def welcome_world ():
world = "Мир"
печать (приветствие, мир)
change_greeting («Привет»)
приветствие_world ()
Привет, мир
Это сработало! Перейдем к следующему сценарию …
Сценарий 2: охват
Здесь мы взглянем на вложенные функции external ()
и inner ()
из примера Enclosing Scope.Давайте попробуем изменить значение first_num
с 1 на 0 изнутри inner ()
.
def external ():
first_num = 1
def inner ():
first_num = 0
second_num = 1
print ("внутреннее - второе_число:", второе_число)
внутренний()
print ("внешний - первое_число:", первое_число)
внешний ()
внутренний - second_num: 1
external - first_num: 1
Не всегда все так просто! Для таких целей мы используем нелокальное ключевое слово в Python.
Нелокальное ключевое слово
Это еще одно удобное ключевое слово, которое позволяет нам более гибко и аккуратно работать с переменными областями действия. Ключевое слово nonlocal полезно во вложенных функциях. Это заставляет переменную ссылаться на ранее связанную переменную в ближайшей охватывающей области. Другими словами, это предотвратит попытку переменной сначала выполнить локальную привязку и заставит ее перейти на уровень «выше». Синтаксис аналогичен ключевому слову global.
def external ():
first_num = 1
def inner ():
нелокальный first_num
first_num = 0
second_num = 1
print ("внутреннее - второе_число:", второе_число)
внутренний()
print ("внешний - первое_число:", первое_число)
внешний ()
внутренний - second_num: 1
внешний - first_num: 0
И вот оно!
Заключение
Теперь вы знаете, какова область видимости переменных Python, правило LEGB и как следует использовать глобальные и нелокальные ключевые слова.Вы сможете легко манипулировать переменными во вложенных функциях без каких-либо проблем. Чтобы узнать больше о программировании на Python, вам обязательно стоит взглянуть на курс DataCamp Intro to Python for Data Science. Это интерактивный курс, охватывающий все основы: от переменных и вычислений до списков, функций и пакетов.
Локальные значения — язык конфигурации
Практическое руководство: Попробуйте упростить конфигурацию Terraform с
Местные жители
учебник по HashiCorp Learn.
Локальное значение присваивает имя выражению,
так что вы можете использовать его несколько раз в модуле, не повторяя
Это.
Если вы знакомы с традиционными языками программирования, это может быть полезно
сравнить модули Terraform с определениями функций:
- Входные переменные подобны аргументам функции.
- Выходные значения аналогичны значениям, возвращаемым функцией.
- Локальные значения подобны временным локальным переменным функции.
Примечание: Для краткости местные значения часто называют просто «местными».
когда смысл ясен из контекста.
»
Объявление локальной ценности
Набор связанных локальных значений может быть объявлен вместе в одном локальном элементе
блок:
местных жителей {
service_name = "форум"
owner = "Команда сообщества"
}
Выражения в локальных значениях не ограничиваются литеральными константами; они могут
также ссылаться на другие значения в модуле, чтобы преобразовать или объединить их,
включая переменные, атрибуты ресурсов или другие локальные значения:
местных жителей {
# Идентификаторы нескольких наборов экземпляров EC2, объединенных вместе
instance_ids = concat (aws_instance.синий. *. id, aws_instance.green. *. id)
}
locals {
# Общие теги, назначаемые всем ресурсам
common_tags = {
Service = local.service_name
Владелец = local.owner
}
}
»
Использование локальных значений
После объявления локального значения вы можете ссылаться на него в
Выражения как local.
.
Примечание: Локальные значения созданы блоком locals
(множественное число), но вы
ссылаются на как на атрибуты объекта с именем local
(в единственном числе).Убедись
чтобы опустить «s» при ссылке на локальное значение!
ресурс "aws_instance" "example" {
# ...
tags = local.common_tags
}
Доступ к локальному значению возможен только в выражениях внутри модуля, в которых оно
было объявлено.
»
Когда использовать локальные значения
Локальные значения могут помочь избежать повторения одних и тех же значений или выражений.
несколько раз в конфигурации, но при чрезмерном использовании они также могут
конфигурацию трудно прочитать будущим сопровождающим из-за сокрытия фактических значений
использовал.
Используйте локальные значения только в умеренных количествах, в ситуациях, когда одно значение или
результат используется во многих местах и , это значение, вероятно, будет изменено в
будущее. Возможность легко изменить значение в центральном месте — ключ к успеху.
преимущество местных ценностей.
Что такое локальная переменная?
Что означает локальная переменная?
Локальная переменная в C # — это тип переменной, объявленной посредством объявления локальной переменной в начале блока, для которого переменная предназначена быть локальной.Он также может встречаться в операторе for, операторе switch, операторе foreach, операторе using, операторе specific-catch или операторе using.
Объявление локальной переменной явно определяет тип переменной, которая была объявлена вместе с идентификатором, который называет переменную.
Локальная переменная — это тип переменной, которая может использоваться там, где область и экстент переменной находятся в пределах метода или блока оператора, в котором она объявлена. Он используется как переменная итерации в операторе foreach, переменная исключения в предложении specific-catch и переменная ресурса в операторе using.Его также можно использовать как константу, значение которой не может быть изменено в блоке метода или инструкции, в котором она объявлена.
Неявно типизированная локальная переменная, тип которой определяется компилятором из выражения справа, полезна при работе с языковыми интегрированными запросами (LINQ), которые возвращают анонимные типы при создании настраиваемого типа для каждого набора результатов LINQ.
Techopedia объясняет локальную переменную
Выделение памяти локальной переменной зависит от ее типа.В случае локальной переменной с типом значения (таких типов, как структура, целое число и т. Д.) Все содержимое сохраняется в стеке, тогда как типизированная по ссылке переменная хранится таким образом, что ее ссылочная часть находится в стеке, а ее содержимое — в куче.
Неявно типизированная локальная переменная объявляется без использования типа .NET, но с ключевым словом «var», которое присваивает соответствующий тип. Например, неявно типизированная локальная переменная может использоваться в операторе foreach без объявления ее типа для итерации коллекции.
Локальная переменная не должна упоминаться в коде в текстовой позиции, которая предшествует оператору объявления этой локальной переменной. Кроме того, в одном блоке не может быть двух или более локальных переменных с одинаковыми именами, так как это может привести к ошибке компиляции. Несколько локальных переменных одного типа могут быть объявлены и инициализированы в одном операторе.
Внутри метода класса, имеющего локальную переменную с именем, аналогичным ее полю, локальная переменная скрывает поле при доступе к нему внутри метода.Более эффективно использовать локальную переменную, чем поле.
Локальные переменные
Локальные переменные
Когда вы объявляете переменные внутри определения функции, они никак не связаны
к другим переменным с такими же именами, используемыми вне функции, т.е. имена переменных
локальный в функцию. Это называется прицел
переменной. Все переменные имеют область видимости блока, в котором они объявлены при запуске.
с точки зрения определения названия.
Пример 7.3. Использование локальных переменных
def func (x): напечатайте 'x is', x х = 2 print 'Изменен локальный x на', x х = 50 func (x) print 'x is still', x
$ python func_local.py х 50 Изменен локальный x на 2 х все еще 50
В функции мы впервые используем значение
имени x
, Python использует значение параметра
объявлен в функции.
Затем мы присваиваем значение 2
x
.Имя x
является локальным для нашей функции.
Итак, когда мы меняем значение x
в функции,
x
, определенные в основном блоке, остаются без изменений.
В последнем заявлении print
мы подтверждаем, что значение
x
в основном блоке практически не изменится.
Использование глобального оператора
Если вы хотите присвоить значение имени, определенному вне функции, тогда вы
должен сказать Python, что имя не локальное, а глобальное .Мы делаем это с помощью глобального оператора
. Невозможно назначить
значение переменной, определенной вне функции, без глобального
утверждение.
Вы можете использовать значения таких переменных, определенные вне функции (при условии, что
не является переменной с таким же именем внутри функции). Однако это не
рекомендуется, и этого следует избегать, поскольку читателю программы становится неясно.
относительно того, где находится определение этой переменной.Использование global
оператор ясно дает понять, что переменная определена во внешнем блоке.
Пример 7.4. Использование глобального оператора
def func (): глобальный x напечатайте 'x is', x х = 2 print 'Изменен глобальный x на', x х = 50 func () print 'Значение x равно', x
$ python func_global.py х 50 Изменен глобальный x на 2 Значение x равно 2
Заявление global
используется для обозначения того, что
x
— глобальная переменная — следовательно, когда мы присваиваем значение
до x
внутри функции, это изменение отражается
когда мы используем значение x
в основном блоке.
Вы можете указать более одной глобальной переменной, используя одну и ту же
глобальный отчет
. Например,
глобальные x, y, z
.
Использование переменных | Центр обучения Postman
Переменные позволяют сохранять и повторно использовать значения в ваших запросах и сценариях. Сохраняя значение в переменной, вы можете ссылаться на него в своих коллекциях, средах и запросах — и если вам нужно обновить значение, вам нужно изменить его только в одном месте.Использование переменных увеличивает вашу способность работать эффективно и сводит к минимуму вероятность ошибки.
Быстрый запуск переменных
Чтобы опробовать переменную, выполните следующие действия:
- Щелкните быстрый просмотр среды (кнопка глаза) в правом верхнем углу Postman и щелкните Edit рядом с Globals .
- Добавьте переменную с именем
my_variable
и присвойте ей начальное значениеHello
— щелкните Save и закройте модальное окно среды. - Откройте новую вкладку запроса и введите
https://postman-echo.com/get?var={{my_variable}}
в качестве URL-адреса. Наведите курсор на имя переменной, и вы увидите значение. - Отправьте запрос. В ответ вы увидите, что Почтальон отправил значение переменной в API. Попробуйте изменить значение в быстром просмотре среды и отправить запрос еще раз.
Читайте подробнее о том, как использовать переменные в Postman.
Содержание
Общие сведения о переменных
Переменная — это символическое представление данных, позволяющее получить доступ к значению без необходимости вводить его вручную, где бы оно ни было.Это может быть полезно, особенно если вы используете одни и те же значения в нескольких местах. Переменные делают ваши запросы более гибкими и читаемыми, абстрагируя некоторые детали.
Например, если у вас один и тот же URL-адрес в нескольких запросах, но URL-адрес может измениться, вы можете сохранить его в переменной. Если URL-адрес изменится, вам нужно только изменить значение переменной, и оно будет отражено во всей вашей коллекции, где бы вы ни использовали имя переменной. Тот же принцип применяется к любой части вашего запроса, где данные повторяются.
Postman поддерживает переменные в разных областях, что позволяет адаптировать обработку к различным задачам разработки, тестирования и совместной работы. Области в Postman относятся к различным контекстам, в которых выполняются ваши запросы — в Postman, в коллекциях, в средах и в Newman / Collection Runner. Вы можете использовать переменные для передачи данных между запросами и тестами, например, если вы связываете запросы с помощью коллекции.
Почтальон будет хранить переменные среды и глобальные переменные в виде строк.Если вы храните объекты или массивы, не забудьте:
JSON.stringify (),
их перед сохранением иJSON.parse ()
при их извлечении.
Переменные в Postman — это пары ключ-значение. Каждое имя переменной представляет свой ключ, поэтому ссылка на имя переменной позволяет вам получить доступ к ее значению.
Например, если у вас есть базовый URL-адрес для запросов, хранящийся в переменной с именем
base_url
, вы можете ссылаться на него в своих запросах, используя{{base_url}}
.Какое бы значение ни было сохранено в переменной, оно будет включено везде, где вы ссылались на переменную при выполнении ваших запросов. Если значение базового URL-адреса —https://httpbin.org
и указано как часть URL-адреса запроса с использованием{{base_url}} / get? Customers = new
, Почтальон отправит запрос наhttps: / /httpbin.org/get?customers=new
.
Вы можете использовать среды для группировки наборов переменных и обмена ими с соавторами, например, если вы используете один набор деталей конфигурации для своего производственного сервера, а другой — для тестирования.Ознакомьтесь с разделом «Управление средами», чтобы узнать больше о том, как включить среды в рабочие процессы вашей группы.
Прицелы с переменным диапазоном
Postman поддерживает следующие области действия переменных:
- Глобальный
- Коллекция
- Окружающая среда
- Данные
- Местный
Если переменная с тем же именем объявлена в двух разных областях, будет использоваться значение, хранящееся в переменной с самой узкой областью — например, если есть глобальная и локальная переменные с именем
имя пользователя
, локальное значение будет используется при выполнении запроса.
Выбор переменных
Переменные области подходят для различных задач в Postman:
Глобальные переменные позволяют получать доступ к данным между коллекциями, запросами, тестовыми сценариями и средами. Глобальные переменные доступны во всем рабочем пространстве.
- Поскольку глобальные переменные могут создавать путаницу, вам следует использовать их только экономно — например, для быстрого тестирования чего-либо или когда ваш проект находится на очень ранней стадии прототипирования.
Переменные коллекции доступны во всех запросах в коллекции и не зависят от сред, поэтому не изменяются в зависимости от выбранной среды.
- Переменные коллекции подходят, если вы используете только одну среду, например, для деталей auth / URL.
Переменные среды позволяют адаптировать обработку к различным средам, например, для локальной разработки и тестирования или производства.Одновременно может быть активна только одна среда.
- Если у вас только одна среда, использование переменных коллекции может быть более эффективным, однако среды позволяют указывать уровни доступа на основе ролей.
Локальные переменные являются временными и доступны только в ваших сценариях запроса. Значения локальных переменных ограничены одним запросом или запуском сбора и больше не доступны после завершения выполнения.
- Локальные переменные подходят, если вам нужно значение для переопределения всех других областей действия переменных, но вы не хотите, чтобы значение сохранялось после завершения выполнения.
- Переменные данных поступают из внешних файлов CSV и JSON для определения наборов данных, которые вы можете использовать при запуске коллекций через Newman или Collection Runner.
Определение переменных
Вы можете определять переменные различными способами, в зависимости от того, нужна ли вам глобальная среда / среда или область коллекции.
Чтобы создать переменную в любой области из построителя запросов, выберите необходимые данные, например, в адресе, параметрах, заголовках или теле.
Выберите Установить как переменную > Установить как новую переменную .
Введите имя , проверьте значение и выберите область из раскрывающегося списка. Нажмите Установить переменную .
Не забудьте удалить переменные, которые вы больше не используете.
Определение глобальных переменных и переменных среды
Вы можете создавать и редактировать переменные среды, выбрав Среда слева от Почтальона или используя быстрый просмотр Среда вверху справа.
Вы можете выбрать среду в раскрывающемся списке вверху справа или на левой боковой панели, нажав кнопку с галочкой, чтобы сделать среду активной .
В быстром просмотре среды вы увидите выбранную среду вместе с глобальными переменными в вашей рабочей области. Вы можете редактировать текущее значение для существующей переменной в строке, щелкнув значение. Чтобы добавить переменную, нажмите Изменить рядом со средой или глобальным разделом.
Вы также можете получить доступ к быстрому просмотру с помощью сочетания клавиш
CMD / CTRL + ALT + E
.
Вы можете добавлять и редактировать переменные в средах, только если у вас есть права на редактирование среды в целом. Если у вас есть доступ для просмотра, вы можете обновить только текущее значение существующих переменных. Любые переменные, которые вы редактируете, будут доступны только вам и недоступны для соавторов в вашей рабочей области.
См. Управление средами для получения дополнительной информации о работе со средами в вашей команде.
После того, как вы выбрали область действия, вы можете указать детали переменной.
Вы также можете определять глобальные переменные и переменные среды в сценариях.
Определение переменных коллекции
Вы можете добавить переменные коллекции при создании коллекции или в любое время после этого. Чтобы создать или изменить переменную для существующей коллекции, выберите коллекцию на боковой панели Коллекции слева от Почтальона и выберите Переменные .
Если у вас нет прав на редактирование коллекции, вы увидите кнопку Запросить доступ .Без права редактирования вы не сможете добавлять новые переменные коллекции, обновлять начальные значения или сохранять значения.
Вы можете изменить текущее значение для локального использования, переопределить переменную коллекции, используя переменную среды с тем же именем, или запросить доступ к коллекции для роли Editor .
Вы также можете определять переменные коллекции в скриптах.
Задание переменной детали
Вы можете добавлять и редактировать переменные в любое время. Все, что вам нужно включить для новой переменной, — это имя — вы можете указать начальное значение, но в качестве альтернативы можете установить его позже, в том числе из сценариев.Установите флажок, чтобы включить или отключить переменную.
Начальные значения передаются при совместном использовании коллекции или среды. Текущие значения являются локальными и не синхронизируются и не передаются. См. Раздел Совместное использование и сохранение данных для получения дополнительной информации о локальных и синхронизированных переменных.
Вы можете загрузить глобальные переменные как JSON из Управление средами .
Вы можете установить значения тела ответа для переменных, выделив текст, щелкнув правой кнопкой мыши / CTRL + щелчок и выбрав соответствующую переменную по имени.
Определение переменных в скриптах
Вы можете программно устанавливать переменные в сценариях запроса.
Используйте pm.globals
для определения глобальной переменной:
pm.globals.set ("ключ_переменной", "значение_переменной");
Используйте pm.collectionVariables
для определения переменной коллекции:
pm.collectionVariables.set ("ключ_переменной", "значение_переменной");
Используйте pm.environment
для определения переменной среды (в текущей выбранной среде):
пм.environment.set ("ключ_переменной", "значение_переменной");
Если у вас есть доступ на просмотр, но не на редактирование среды, ваш код сценария будет влиять только на текущее значение и не будет синхронизироваться или предоставлен вашей команде.
Вы можете использовать unset
для удаления переменной:
pm.environment.unset ("ключ_переменной");
Дополнительные сведения о сценариях с переменными см. В Справочнике по песочнице.
Определение локальных переменных
Локальные переменные — это временные значения, которые вы задаете в сценариях запроса, используя следующий синтаксис:
пм.переменные.set ("ключ_переменной", "значение_переменной");
Локальные переменные не сохраняются между сеансами, но позволяют временно переопределить все другие области во время выполнения запроса или запуска сбора / мониторинга. Например, если вам нужно обработать временное тестовое значение для одного запроса или коллекции, запущенной локально, и вы не хотите, чтобы значение синхронизировалось с вашей командой или оставалось доступным после завершения выполнения запроса / коллекции, вы можете использовать локальный Переменная.
Доступ к переменным
Вы можете использовать двойные фигурные скобки для ссылки на переменные в пользовательском интерфейсе Postman.Например, для ссылки на переменную с именем username в настройках аутентификации запроса вы можете использовать следующий синтаксис с двойными фигурными скобками вокруг имени:
Когда вы запускаете запрос, Postman разрешит переменную и заменит ее текущим значением.
Например, у вас может быть URL-адрес запроса, ссылающийся на переменную, как показано ниже:
http://pricey-trilby.glitch.me/customer?id={{cust_id}}
Почтальон отправит любое значение, которое вы в настоящее время сохранили для переменной cust_id
при выполнении запроса.Если cust_id
в настоящее время 3
, запрос будет отправлен по следующему URL-адресу, включая параметр запроса:
http://pricey-trilby.glitch.me/customer?id=3
В качестве альтернативы у вас может быть тело запроса, которое обращается к переменной, заключив ссылку на нее в двойные кавычки:
{"customer_id": "{{cust_id}}"}
Вы можете использовать переменные в URL-адресах запроса, параметрах, заголовках, авторизации, теле и предварительных настройках заголовка.
При наведении курсора на переменную можно увидеть обзор ее текущего состояния. По мере того, как вы вводите переменные в свои запросы, Postman будет предлагать вам любые, которые определены в настоящее время.
В приглашении будет указано текущее значение, область действия (выделена цветом) и статус переопределения, где это необходимо.
Если переменная не разрешена, Почтальон выделит ее красным цветом.
Использование переменных в скриптах
Вы можете получить текущее значение переменной в ваших скриптах, используя объект, представляющий уровень области и .получить
метод:
pm.variables.get ("ключ_переменной");
pm.globals.get ("ключ_переменной");
pm.collectionVariables.get ("ключ_переменной");
pm.environment.get ("ключ_переменной");
Использование
pm.variables.get ()
для доступа к переменным в ваших сценариях дает вам возможность изменять область действия переменных, не влияя на функциональность вашего сценария. Этот метод вернет любую переменную, имеющую в настоящее время наивысший приоритет (или самую узкую область видимости).
Совместное использование и сохранение данных
Когда вы редактируете глобальные переменные, переменные коллекции и среды в Postman, вы увидите Current Value , Persist и Reset options для отдельных переменных и для всех переменных (наведите указатель мыши на переменную и используйте … для сохранения индивидуальных ценностей). Это позволяет вам контролировать, что происходит в вашем локальном экземпляре Postman, независимо от того, как данные синхронизируются с кем-либо, кто использует вашу рабочую область, запросы, коллекции и среды.
Ваш локальный сеанс в Postman может использовать временные значения, которые видны только вам. Это позволяет вам разрабатывать и тестировать, используя личные учетные данные или экспериментальные значения, без риска раскрыть эти детали или повлиять на других в вашей команде.
Например, ваша команда может иметь общий ключ API и отдельные ключи API — вы можете проводить больше экспериментальных разработок локально, используя свой личный ключ, но использовать общий ключ для совместной работы в группе. Точно так же у вас может быть переменная, которая представляет исследовательскую работу, которую вы выполняете локально, но не готовы поделиться с командой — позже вы можете сохранить локальные данные, чтобы другие члены вашей группы также могли получить к ним доступ.
Когда вы создаете или редактируете переменную, вы можете ввести как начальное, так и текущее значение.Если при создании новой переменной в пользовательском интерфейсе оставить текущее значение пустым, оно автоматически заполнится начальным значением. Если вы укажете текущее значение, оно будет локальным только для вашего экземпляра — параметр Persist позволяет вам передать текущее значение в общие данные, обновляя начальное значение, чтобы оно соответствовало текущему значению.
Вы можете редактировать начальное значение переменной среды, только если у вас есть права на редактирование самой среды. Без прав на редактирование среды вы можете редактировать только текущее значение, и ваши изменения не будут видны никому, кто имеет доступ к вашему рабочему пространству.
Использование Persist синхронизирует ваше текущее значение с серверами Postman и отразит его для всех, кто поделится вашей коллекцией или средой. Чтобы сбросить текущие локальные значения для отражения исходных (общих) значений, используйте Reset .
Текущее значение можно отредактировать в режиме быстрого просмотра среды:
Подробнее о работе с переменными в команде см. В разделе «Управление средами».
Локальные переменные и переменные данных имеют только текущие значения, которые не сохраняются после выполнения запроса или сбора.
Регистрируемые переменные
Вы можете записывать значения переменных в Postman Console во время выполнения ваших запросов. Откройте консоль с помощью кнопки в нижнем левом углу Postman или из меню View . Чтобы записать значение переменной, используйте в сценарии следующий синтаксис:
console.log (pm.variables.get ("ключ_переменной"));
Использование переменных данных
Средство выполнения коллекции позволяет импортировать файл CSV или JSON и использовать значения из файла данных в запросах и сценариях.Вы не можете установить переменную данных внутри Postman, потому что она извлекается из файла данных, но вы можете получить доступ к переменным данных внутри скриптов, например, используя pm.iterationData.get ("variable_name")
.
Дополнительные сведения см. В разделе «Работа с файлами данных» и «Справочник по API песочницы».
Использование динамических переменных
Postman предоставляет динамические переменные, которые вы можете использовать в своих запросах.
Примеры динамических переменных:
-
{{$ guid}}
: руководство стиля v4 -
{{$ timestamp}}
: текущая отметка времени (отметка времени Unix в секундах) -
{{$ randomInt}}
: случайное целое число от 0 до 1000
Полный список см. В разделе «Динамические переменные».
Чтобы использовать динамические переменные в сценариях предварительного запроса или тестирования, вам необходимо использовать
pm.