Как сделать функцию глобальной python
Перейти к содержимому

Как сделать функцию глобальной python

  • автор:

Как в python задать глобальную переменную в функции не используя global

Я хочу задать глобальную переменную в функции. Это легко можно сделать с помощью global , но везде пишут, что из-за этого может возникнуть много проблем и вообще во многих случаях лучше не использовать global , считая каждый его вызов ошибкой в коде. Вот условный пример моего кода:

a = '' def func(): a = input() if not a: print('a is empty') else: print('a is not empty') func() 

Этот код условно должен проверять a — это пустая переменная или нет. Как мне сделать, чтобы пустая строка a заменилась на то, что введет пользователь и запомнилась глобально?

Отслеживать
73.1k 107 107 золотых знаков 38 38 серебряных знаков 55 55 бронзовых знаков
задан 18 ноя 2021 в 20:44
21 2 2 бронзовых знака

6 ответов 6

Сортировка: Сброс на вариант по умолчанию

Глобальные переменные плохи не от того, что они задаются описателем global . Они плохи потому, что неявное описание переменных усложняет логику программ, ее читаемость, отлаживаемость и предсказуемость результатов. Вы их или используете (и неважно как ) или нет. А если используете — то точно понимая все риски и точно понимая, зачем вы на них идете. И ответив (хотя-бы себе) на вопрос — а без них можно?

А если делать все «как положено» — то просто передаете переменную в функцию явно, а в функции выполняете свои проверки и модификации. Только и всего — просто и понятно.

Отслеживать
68k 5 5 золотых знаков 20 20 серебряных знаков 51 51 бронзовый знак
ответ дан 18 ноя 2021 в 20:57
11.6k 2 2 золотых знака 10 10 серебряных знаков 16 16 бронзовых знаков

def func(a): # a = input() if not a: print('\na is empty') a = f'a is empty --> ``' else: print('\na is not empty') a = f'a is not empty --> ``' return a a = '' a = func(a) print(f'1. a = ') a = 'Hello' a = func(a) print(f'2. a = ') 

Отслеживать
ответ дан 18 ноя 2021 в 20:57
73.1k 107 107 золотых знаков 38 38 серебряных знаков 55 55 бронзовых знаков

Лучше передать функции эту переменную, провести с ней какие-то действия, и вернуть результат функции через ‘return’, и переписать переменную.

Отслеживать
ответ дан 18 ноя 2021 в 20:56
405 2 2 серебряных знака 7 7 бронзовых знаков
Чем это отличается от уже данного ответа?
18 ноя 2021 в 21:43
Тем что он дан раньше
19 ноя 2021 в 7:46
Ой, сорри. Не заметил из очереди.
19 ноя 2021 в 7:52
Ничего страшного)
19 ноя 2021 в 8:09

Вынеси переменную за блок функций

a = 1 b = 2 def ab(): print(a + b) 

Отслеживать
ответ дан 19 ноя 2021 в 4:02
IPOleksenko IPOleksenko
82 9 9 бронзовых знаков

Ну это на самом деле и есть глобальные переменные. Просто пока вы их не модифицируете в функции, питон не требует использовать в ней ключевое слово global

19 ноя 2021 в 4:16

Помимо уже написанного про функции и передачу параметром, есть случаи, когда вам нужно, например, хранить некие параметры, к которым должен быть доступ сразу у многих ваших функций или классов. В этом случае, конечно, неудобно передавать эти параметры в явном виде в каждую функцию.

Вот в этом случае можно завести, например, специальный класс, который хранит эти параметры, а все ваши функции берут параметры из полей этого класса. А сам этот класс может загружать параметры из файла, например. Класс этот можно вынести в отдельный файл и импортировать этот файл когда нужны эти параметры.

Хотя по сути класс — это тоже в общем-то глобальная переменная, но класс — это лучше, чем переменная, переменные обычно называют абы как, а у класса скорее всего будет выделенное «говорящее» название и в одном классе будет собрано много параметров, а не так, что много глобальных переменных непонятно откуда взявшихся, ищи потом, откуда они, как и чего.

Ещё в случае, если вам нужно менять какие-то переменные в нескольких функциях, удобно вынести эти переменные в какой-то класс опять же, а функции сделать методами этого класса. Тогда эти функции-методы будут иметь прямой доступ к этим переменным-полям, при этом они не будут засорять этими переменными глобальное пространство имён и мешать другим функциям/классам. Чем и хороша собственно инкапсуляция — один из трёх принципов, на которых стоит ООП.

Локальные и глобальные переменные в Python

При программировании на Python, как и во всяком другом языке всегда крайне важно четко понимать сущность, отличия, нюансы применения и возможные последствия от использования переменных различных областей видимости. Ведь, по большому счету, от такого четкого понимания роли различных типов переменных в конечном счете зависит структура и логика наших программ в целом, то какие пользовательские классы, объекты и модули мы в этих программах сформируем.

В Python для переменных существует 4-ре следующие области видимости: локальная, нелокальная, глобальная и системная.

Системная область видимости шефствует над системными переменными из операционок ваших вычислительных средств и является наиболее универсальной, поскольку обеспечивает доступ к этим переменным из любого модуля или функции в ваших программах. В тоже время, переменные из данной области видимости относительно не часто используются в кодинге и, в основном применяются при разработке системных утилит или программ тесно связанных с возможностями операционных систем, в которых они выполняются.

Еще одной довольно специфической в применении является нелокальная область видимости, переменные которой используются главным образом во вложенных функциях тогда, когда необходимо указать, что для конкретной вложенной функции определенная переменная не является ни локальной, ни глобальной с точки зрения всей программы (модуля).

Таким образом, априори можно констатировать, что наиболее распространенными являются локальные и глобальные переменные, о нюансах применения которых пойдет речь далее в этой статье.

Определение локальных и глобальных переменных

Глобальной переменной называется та переменная, доступ к которой можно получить из любого места в коде вашего модуля – отдельного программного файла. Объявление этих переменных производится вне любых программных блоков типа функций, классов или процедур и, обычно помещается в начало файла с программным кодом.

Важно отметить, что программа на Python может состоят из нескольких отдельных программных файлов (нескольких модулей), но переменная будет являться глобальной лишь в том модули, в котором она была объявлена. Из других модулей, независимо от того являются ли они частями одной программы или нет, данная переменная видна не будет.

Локальной переменной может быть любая переменная, объявленная внутри какого-либо программного блока типа функции, класса, метода или процедуры. Основным отличием локальных переменных от глобальных является то, доступ к первым может осуществляться лишь в том программном блоке, в котором они были объявлены.

Чтение глобальных переменных

Давайте в самом начале нашей программы объявим переменную privetstviye , которую инициализируем строкой “Привет земляне!”:

>>> privetstviye = "Привет земляне!" 

Затем, давайте объявим простенькую функцию prosmotr_privetstviya() , которая будет выводить на печать нашу раннее объявленную переменную privetstviye :

>>> def prosmotr_privetstviya(): . print(privetstviye) . 

Как и следовало ожидать, в результате вызова вышеназванной функции мы увидим выведенную на дисплей строку “Привет земляне!”:

>>> prosmotr_privetstviya() Привет земляне! 

Хотя переменная privetstviye в функции prosmotr_privetstviya() и не была объявлена, данная функция все-таки без проблем смогла ее вывести внутри себя. Это объясняется тем, что данную переменную мы объявили в глобальной области видимости — в начале программы и вне всяких программных блоков. Такое место объявления нашей переменной автоматически придало ей статус глобальной – видимой во всех программных блоках, имеющихся в данном модули (программном файле).

Изменение значений у локальных и глобальных переменных

Как мы уже знаем, всегда при объявлении переменных внутри каких-либо программных блоков, Python присваивает этим переменным статус локальных, то есть тех, которые являются доступными лишь внутри блока, где они были объявлены. Кроме того, те переменные, которые передаются пользовательским функциям в качестве аргументов, для самых этих функций также являются локальными.

На примерах раннее мы уже выяснили, что внутри программных блоков (функций) запросто можно использовать глобальные переменные для вывода их значений. Теперь же давайте выясним, можно ли внутри таких функций изменять значения глобальным переменным. Для этого объявим дополнительную пользовательскую функцию izmenyayemoye_privetstviya() , которая принимая в качестве своего аргумента utochneniye уточнение для приветствия, будет конкатенировать (объединять) его вместе со словом “Привет” и присваивать полученное значение нашей “якобы” глобальной переменной privetstviye из предыдущего раздела нашей статьи. В дополнение к этому вышеназванная функция буде еще и выводить получившиеся значение из переменной privetstviye на дисплей:

>>> def izmenyayemoye_privetstviya(utochneniye): . privetstviye = f"Привет utochneniye>!" . print(privetstviye) . 

Как вы думаете, что нам выведет функция izmenyayemoye_privetstviya() с переданным, например, аргументом “инопланетяне”, а что покажет непосредственно сама переменная privetstviye , выведенная через стандартную функцию print(). Давайте для наглядности приведем полный листинг кода с этим примером:

privetstviye = "Привет земляне!" def izmenyayemoye_privetstviya(utochneniye): privetstviye = f"Привет utochneniye>!" print(privetstviye) izmenyayemoye_privetstviya("инопланетяне") print(privetstviye) # Привет инопланетяне! # Привет земляне! 

Из приведенного выше примера четко видно, что в программах на Python вполне себе могут уживаться глобальные и локальные переменные имеющие одинаковые имена. Но, вот как раз по этой причине, Python не позволяет поменять значения у глобальных переменных внутри каких-либо программных блоков. Как только мы попытаемся это сделать внутри одного из таких блоков, наш любимый интерпретатор именно в этом блоке объявить дополнительную переменную с тем же именем, но не с глобальной, а с локальной областью видимости.

Затенение переменных

Итак, мы уже выяснили, что внутри каких-либо программных блоков значения, как глобальных, так и локальных переменных являются равнодоступными только для чтения. При этом, если у нас глобальная и локальная переменные имеют одинаковые имена и, мы попытаемся одну из этих переменных прочитать внутри какой-либо функции, класса или любого другого программного блока, то приоритет всегда будет отдан именно локальной переменной.

Если же внутри программного блока нам нужно не прочитать, а изменить значение какой-либо переменной, то даже когда данная переменная является у нас глобальной, мы фактически способны изменить (инициализировать) исключительно только локальную переменную.

Иногда в Python специально для оптимизации кода используют вышеописанные особенности сочетания одноименных глобальных и локальных переменных, а сам процесс создания этих переменных с одинаковыми именами называют затенением. Вмести с тем, следует отметить, что многие опытные разработчики все-таки не склонны применять практику затенения переменных поскольку видят в ней негативное влияние на читабельность и логику восприятия разрабатываемого ими кода.

Совместное использование локальных и глобальных переменных

Интуитивно, на уровне подсознание, мы где-то уже почти осознали то, что внутри любого программного блока априори не может быть двух переменных с одинаковыми именами, одна из которых трактовалась бы интерпретатором, как глобальная, а другая, как локальная переменная. Но, давайте посмотрим, как будет реагировать наш интерпретатор в случае реального наличия такой ситуации. Для этого немного изменим раннее объявленную нами функцию izmenyayemoye_privetstviya() таким образом, чтобы прежде она выводила бы на дисплей строку приветствия из нашей глобальной переменной privetstviye , а затем переменной с тем же именем присваивала бы другую строку приветствие сформированную с учетом значения, полученного из своего аргумента. В конце же данной функции мы снова попытаемся вывести уже измененное содержимое переменной privetstviye на дисплей.

>>> def izmenyayemoye_privetstviya(utochneniye): . print(privetstviye) . privetstviye = f"Привет utochneniye>!" . print(privetstviye) . 

По идее можно было бы предположить, что в результате вызова только что откорректированной нами функции с аргументом:

izmenyayemoye_privetstviya("инопланетяне") 

у нас на дисплее сначала выведется содержание нашей предварительно инициализированной глобальной переменной (Привет земляне!), а затем содержание нашей локальной переменной (Привет инопланетяне!), которая была инициализирована прямо внутри нашей функции. Но, на самом деле произойдет нечто иное:

privetstviye = "Привет земляне!" def izmenyayemoye_privetstviya(utochneniye): print(privetstviye) privetstviye = f"Привет utochneniye>!" print(privetstviye) izmenyayemoye_privetstviya("инопланетяне") # Traceback (most recent call last): # File "", line 8, in # File "", line 4, in izmenyayemoye_privetstviya # UnboundLocalError: cannot access local variable 'privetstviye' where it is not associated with a value 

Фактически, при вызове первого print(privetstviye) в функции izmenyayemoye_privetstviya() мы в нашем примере получили исключение UnboundLocalError, гласящее о том, что локальная переменная privetstviye не может быть выведена на дисплей, так как она еще не инициализирована.

На первый взгляд данная ошибка может показаться несуразностью, ведь мы предварительно объявили и инициализировали privetstviye вне всяких программных блоков, как глобальную переменную. А тут, в ошибке выдается, что она якобы превратилась в локальную, да еще не инициализированную переменную? Но, на самом деле это объясняется тем, что, анализируя код нашей функции, интерпретатор Python прежде всего просматривает операторы присваивания и, только потом отслеживает имена переменных, которым в нашем случае мы присвоили одинаковые имена privetstviye. Именно поэтому, видя в нашей функции оператор присвоения в отношении к переменной privetstviye, интерпретатор не только априори объявляет эту переменную локальной, но и любое другое упоминание ее имени в функции также воспринимает в качестве этой же локальной переменной.

Таким образом, в 4-й строке программы вместе со стандартной функцией print() у нас используется локальная переменная privetstviye, которая фактически инициализируется только на следующей, 5-й строке.

В общем случае необходимо отметить, что любое присвоение внутри программного блока делает из одноименной глобальной переменной, локальную. Следовательно, если эта переменная упоминается в программном блоке прежде, чем она была инициализирована, то это вызовет ошибку.

Выводы

Из данной статьи в отношении интерпретатора Python можно сделать следующие обобщающие выводы:

  1. Использование значений глобальных переменных допускается в любом месте модуля (файла с программой), как внутри программных блоков (функций, классов, методов или процедур), так и вне их.
  2. Значения локальных переменных внутри тех программных блоков, где они были объявлены могут без ограничений, как использоваться, так и изменяться.
  3. В случае наличия в модули одноименных глобальных и локальных переменных, внутри программных блоков могут быть доступны лишь значения объявленных там локальных переменных. Этот процесс называется затенением переменных.
  4. Внутри программных блоков каждая переменная должна однозначно восприниматься интерпретатором, либо как локальная, либо как глобальная. Двух одноименных переменных с разными областями видимости в интерпретаторе быть не может.
  5. Оператор присвоения внутри программных блоков в случае одноименных глобальных и локальных переменных всегда глобальную переменную замещает локальной, что может привести к ошибки при использовании данной переменной до ее инициализации.

Область видимости переменных в Python

Область видимости переменной указывает область, в которой мы можем получить доступ к переменной. В Python мы можем объявлять переменные в трех разных областях видимости: локальной, глобальной и нелокальной.

Типы переменных в Python:

  1. Локальные переменные
  2. Глобальные переменные
  3. Нелокальные переменные

Локальные переменные в Python

Когда мы объявляем переменные внутри функции, эти переменные будут иметь локальную область видимости. Мы не можем получить к ним доступ вне функции. Эти типы переменных называются локальными переменными. Например:

# Локальная переменная
message = ‘Hello’
print ( ‘Local’ , message )
# Пытаемся получить доступ к переменной message
# вне функции greet()
print ( message )

Local Hello
NameError: name ‘message’ is not defined

Здесь переменная message является локальной переменной функции greet(), поэтому доступ к ней возможен только внутри этой функции. Именно поэтому в результате мы увидели сообщение об ошибке, когда попытались получить доступ к переменной message вне функции greet().

Чтобы решить эту проблему, мы можем сделать переменную message глобальной.

Глобальные переменные в Python

В Python переменная, объявленная вне функции или в глобальной области видимости, называется глобальной переменной. Это означает, что глобальная переменная может быть доступна как внутри, так и вне функции.

Давайте рассмотрим пример создания глобальной переменной в Python:

Кратко о локальных и глобальных переменных

Подробнее о 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 f() print(a) # ошибка, локальная переменная а не видна вне функции f. 

Ошибка «builtins.NameError: name ‘a’ is not defined»

  • Локальные переменные создаются =.
  • Каждый вызов функции создает локальную переменную (свою, новую) (каждый вызов функции создает свой новый namespace)
  • после завершения функции ее локальные переменные уничтожаются.
  • аргументы функции тоже являются локальными переменными (при вызове функции идет = параметру значения).

Итого: Если в функции было =, то мы создали локальную переменную. Если = не было, то читаем глобальную переменную.

Можно создавать в разных функциях локальные переменные с одинаковыми именами. В функциях foo и bar создали переменные с одинаковыми именами а.

Можно (но не надо так делать!) создавать локальную переменную с тем же именем, что и глобальную. pylint поможет найти такие переменные.

def f(): a = 1 # создана локальная переменная а=1 print(a, end=' ') # печатаем локальную переменную а=1 a = 0 # создана глобальная переменная а=0 f() print(a) # печатаем глобальную переменную а=0 
  1. создается глобальная переменная а = 0
  2. вызывается f()
  3. в f создается локальная переменная а = 1 (теперь нельзя доступиться из функции f к глобальной переменной a)
  4. в f печатается локальная переменная a = 1
  5. завершается f
  6. печатается глобальная переменная а = 0

Переменная в функции будет считаться локальной, если она будет создана внутри условного оператора, который никогда не выполнится:

def f(): print(a) # UnboundLocalError: local variable 'a' referenced before assignment if False: a = 0 # тут создаем локальную переменную а внутри функции f 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() # Выведет 88: локальная переменная в объемлющей функции f2() # Ошибка! функция f2 здесь не видна! 

В f2() нельзя изменить значение Х, принадлежащей функции f1(). Вместо этого будет создана еще одна локальная переменная, но уже в пространстве имен функции f2(). Напечатает 77 88 :

X = 99 # Имя в глобальной области видимости: не используется def f1(): X = 88 # Локальное имя в объемлющей функции def f2(): X = 77 # создаем локальную переменную print(X) # 77 - обращение к локальной переменной функции f2() f2() print(X) # 88 - обращение к локальной переменной функции f1() f1() 

Если нужно изменять значение переменной Х, которая принадлежит пространству имен объемлющей (enclosed) функции, то добавляют unlocal

X = 99 # Имя в глобальной области видимости: не используется def f1(): X = 88 # Локальное имя в функции f1 def f2(): unlocal X # X принадлежит объемлющей функции X = 77 # изменяем переменную функции f1 print(X) # 77 - обращение к локальной переменной объемлющей функции f1() f2() print(X) # 77 - обращение к локальной переменной функции f1() f1() 

Правило LEGB

При определении, к какому namespace относится имя, используют правило LEGB:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *