Использование глобальных переменных в функции


3117

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

Если я создаю глобальную переменную в одной функции, как я могу использовать эту глобальную переменную в другой функции? Нужно ли хранить глобальную переменную в локальной переменной функции, которой нужен доступ?

Ответы:


4249

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

globvar = 0

def set_globvar_to_one():
    global globvar    # Needed to modify global copy of globvar
    globvar = 1

def print_globvar():
    print(globvar)     # No need for global declaration to read value of globvar

set_globvar_to_one()
print_globvar()       # Prints 1

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

Смотрите другие ответы, если вы хотите поделиться глобальной переменной между модулями.


839
Сильно преувеличение называть глобалы «такими опасными». Глобалы прекрасно работают на любом языке, который когда-либо существовал и будет существовать. У них есть свое место. Вы должны были сказать, что они могут вызвать проблемы, если вы не знаете, как программировать.
Энтони

208
Я думаю, что они довольно опасны. Однако в python «глобальные» переменные на самом деле являются модульными, что решает множество проблем.
Фабио Сантос

247
Я не согласен с тем, что Python требует globalключевое слово потому, что глобальные переменные опасны. Скорее, это потому, что язык не требует, чтобы вы явно объявляли переменные, и автоматически предполагает, что переменная, которую вы назначаете, имеет область действия функции, если вы не укажете иное. globalКлючевое слово является средством , которое предоставляется сказать иначе.
Nate CK

7
@avgvstvs: И если вы реализуете ту же программу без глобальных переменных, у вас все равно будет одинаковое количество путей кода. Вы привели аргумент против глобалов.
Гонки

13
@LightnessRacesinOrbit Я не понимаю твою точку зрения. Если вы удаляете глобальную переменную, вы удаляете усложняющий фактор, заключающийся в том, что теперь произвольные функции больше не могут изменять состояние программы в различных точках выполнения - таким образом изменяя выполнение таким образом, который в противном случае будет незаметен для других функций, зависящих от этой переменной. Вам больше не нужно следить за тем, «Изменилось ли f2()состояние, чтобы теперь f3()могло произойти что-то неожиданное? Функции теперь могут работать независимо от состояния внешней программы.
avgvstvs

775

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

Скажем, у вас есть такой модуль:

# sample.py
myGlobal = 5

def func1():
    myGlobal = 42

def func2():
    print myGlobal

func1()
func2()

Вы могли бы ожидать, что это напечатает 42, но вместо этого оно печатает 5. Как уже упоминалось, если вы добавите объявление ' global' func1(), то вы func2()получите 42.

def func1():
    global myGlobal
    myGlobal = 42

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

myGlobalПоэтому, когда вы назначаете 42 имени , Python создает локальную переменную, которая скрывает глобальную переменную с тем же именем. Это местный выходит из области видимости и сборки мусора , когда func1()возвращается; Между тем, func2()никогда не вижу ничего, кроме (неизмененного) глобального имени. Обратите внимание, что это решение пространства имен происходит во время компиляции, а не во время выполнения - если бы вы прочитали значение myGlobalinside func1()перед тем, как присваивать ему, вы бы получили UnboundLocalError, потому что Python уже решил, что это должна быть локальная переменная, но это не имеет никакого значения, связанного с этим еще. Но используя globalоператор ' ', вы говорите Python, что он должен искать имя в другом месте, а не присваивать ему локально.

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


Вы упомянули, что решение о пространстве имен происходит во время компиляции , я не думаю, что это правда. Из того, что я узнал, компиляция Python проверяет только синтаксическую ошибку, а не ошибку имени, попробуйте этот пример def A (): x + = 1 , если вы его не запускаете, он не выдаст UnboundLocalError , пожалуйста, проверьте спасибо
watashiSHUN

1
Распространено использовать заглавную букву для глобальных переменных, таких какMyGlobal = 5
Vassilis

3
@watashiSHUN: Решение имен это произойдет во время компиляции. Решение о том, что xлокально, отличается от проверки во время выполнения, было ли локальное имя связано со значением до того, как оно использовалось в первый раз.
Блэкджек

9
@Vassilis: Все буквы в верхнем регистре являются общими MY_GLOBAL = 5. Смотрите руководство по стилю для кода Python .
Блэкджек

223

Вы можете исследовать понятие пространств имен . В Python модуль является естественным местом для глобальных данных:

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

Конкретное использование global-in-a-module описано здесь - Как мне совместно использовать глобальные переменные между модулями? и для полноты содержания поделитесь здесь:

Канонический способ обмена информацией между модулями в рамках одной программы - создать специальный модуль конфигурации (часто называемый config или cfg ). Просто импортируйте модуль конфигурации во все модули вашего приложения; модуль становится доступным как глобальное имя. Поскольку существует только один экземпляр каждого модуля, любые изменения, внесенные в объект модуля, отражаются повсеместно. Например:

Файл: config.py

x = 0   # Default value of the 'x' configuration setting

Файл: mod.py

import config
config.x = 1

Файл: main.py

import config
import mod
print config.x

1
по причине, по которой мне не нравится, config.x могу ли я от нее избавиться? Я пришел с, x = lambda: config.xа затем у меня есть новое значение в x(). по какой-то причине наличие a = config.xне помогает мне.
Владозавр

3
@vladosaurus это from config import xрешает?
Jhylands

93

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

>>> import dis
>>> def foo():
...     global bar
...     baz = 5
...     print bar
...     print baz
...     print quux
... 
>>> dis.disassemble(foo.func_code)
  3           0 LOAD_CONST               1 (5)
              3 STORE_FAST               0 (baz)

  4           6 LOAD_GLOBAL              0 (bar)
              9 PRINT_ITEM          
             10 PRINT_NEWLINE       

  5          11 LOAD_FAST                0 (baz)
             14 PRINT_ITEM          
             15 PRINT_NEWLINE       

  6          16 LOAD_GLOBAL              1 (quux)
             19 PRINT_ITEM          
             20 PRINT_NEWLINE       
             21 LOAD_CONST               0 (None)
             24 RETURN_VALUE        
>>> 

Посмотрите, как foo()единственная LOAD_FASTпеременная - baz, которая появляется в левой части назначения .


12
Эвристика ищет обязательные операции . Назначение - это одна такая операция, импорт другой. Но цель forцикла и имя после операторов asin withи exceptтакже связаны с.
Мартин Питерс

@MartijnPieters Для имени после asв exceptпредложении это не было очевидно для меня. Но он автоматически удаляется для экономии памяти.
Роберт

1
@ Роберт: не для сохранения памяти, а для избежания создания круговой ссылки, которая может привести к утечкам памяти. Это связано с тем, что исключение ссылается на трассировку, а трассировка ссылается на каждое локальное и глобальное пространство имен по всему стеку вызовов, включая as ...цель в обработчике исключений.
Мартин Питерс

62

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

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

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


Абсолютно ре. фанатики. Большинство пользователей Python используют его для создания сценариев и создают небольшие функции для выделения небольших кусочков кода.
Пол Ушак

51

Если я создаю глобальную переменную в одной функции, как я могу использовать эту переменную в другой функции?

Мы можем создать глобал с помощью следующей функции:

def create_global_variable():
    global global_variable # must declare it to be a global first
    # modifications are thus reflected on the module's global scope
    global_variable = 'Foo' 

Написание функции фактически не запускает ее код. Итак, мы вызываем create_global_variableфункцию:

>>> create_global_variable()

Использование глобалов без изменений

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

Например,

def use_global_variable():
    return global_variable + '!!!'

и теперь мы можем использовать глобальную переменную:

>>> use_global_variable()
'Foo!!!'

Модификация глобальной переменной изнутри функции

Чтобы указать глобальную переменную на другой объект, необходимо снова использовать ключевое слово global:

def change_global_variable():
    global global_variable
    global_variable = 'Bar'

Обратите внимание, что после написания этой функции код, фактически изменяющий ее, все еще не выполняется:

>>> use_global_variable()
'Foo!!!'

Итак, после вызова функции:

>>> change_global_variable()

мы можем видеть, что глобальная переменная была изменена. global_variableИмя теперь указывает на 'Bar':

>>> use_global_variable()
'Bar!!!'

Обратите внимание, что «глобальный» в Python не является действительно глобальным - он только глобален для уровня модуля. Так что он доступен только для функций, написанных в модулях, в которых он является глобальным. Функции запоминают модуль, в котором они написаны, поэтому, когда они экспортируются в другие модули, они все равно ищут модуль, в котором они были созданы, чтобы найти глобальные переменные.

Локальные переменные с тем же именем

Если вы создадите локальную переменную с тем же именем, она заменит глобальную переменную:

def use_local_with_same_name_as_global():
    # bad name for a local variable, though.
    global_variable = 'Baz' 
    return global_variable + '!!!'

>>> use_local_with_same_name_as_global()
'Baz!!!'

Но использование этой неправильно названной локальной переменной не меняет глобальную переменную:

>>> use_global_variable()
'Bar!!!'

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

Мы получаем то же самое поведение в классах

Последующий комментарий комментирует:

Что делать, если я хочу создать глобальную переменную внутри функции внутри класса и использовать эту переменную внутри другой функции внутри другого класса?

Здесь я демонстрирую, что мы получаем то же поведение в методах, что и в обычных функциях:

class Foo:
    def foo(self):
        global global_variable
        global_variable = 'Foo'

class Bar:
    def bar(self):
        return global_variable + '!!!'

Foo().foo()

И сейчас:

>>> Bar().bar()
'Foo!!!'

Но я бы предложил вместо использования глобальных переменных использовать атрибуты класса, чтобы не загромождать пространство имен модуля. Также обратите внимание, что selfздесь мы не используем аргументы - это могут быть методы класса (удобно, если мутирует атрибут класса из обычного clsаргумента) или статические методы (нет selfили cls).


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

2
@anonmanx Я не знаю, почему вы застряли, в методе такое же поведение, как и в обычной функции. Но я обновлю свой ответ вашим замечанием и некоторым демонстрационным кодом, хорошо?
Аарон Холл

1
@anonmanx как это?
Аарон Холл

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

47

В дополнение к уже существующим ответам и сделать это более запутанным:

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

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

Источник: Каковы правила для локальных и глобальных переменных в Python? ,


34

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

import multiprocessing
import os
import random
import sys
import time

def worker(new_value):
    old_value = get_value()
    set_value(random.randint(1, 99))
    print('pid=[{pid}] '
          'old_value=[{old_value:2}] '
          'new_value=[{new_value:2}] '
          'get_value=[{get_value:2}]'.format(
          pid=str(os.getpid()),
          old_value=old_value,
          new_value=new_value,
          get_value=get_value()))

def get_value():
    global global_variable
    return global_variable

def set_value(new_value):
    global global_variable
    global_variable = new_value

global_variable = -1

print('before set_value(), get_value() = [%s]' % get_value())
set_value(new_value=-2)
print('after  set_value(), get_value() = [%s]' % get_value())

processPool = multiprocessing.Pool(processes=5)
processPool.map(func=worker, iterable=range(15))

Вывод:

before set_value(), get_value() = [-1]
after  set_value(), get_value() = [-2]
pid=[53970] old_value=[-2] new_value=[ 0] get_value=[23]
pid=[53971] old_value=[-2] new_value=[ 1] get_value=[42]
pid=[53970] old_value=[23] new_value=[ 4] get_value=[50]
pid=[53970] old_value=[50] new_value=[ 6] get_value=[14]
pid=[53971] old_value=[42] new_value=[ 5] get_value=[31]
pid=[53972] old_value=[-2] new_value=[ 2] get_value=[44]
pid=[53973] old_value=[-2] new_value=[ 3] get_value=[94]
pid=[53970] old_value=[14] new_value=[ 7] get_value=[21]
pid=[53971] old_value=[31] new_value=[ 8] get_value=[34]
pid=[53972] old_value=[44] new_value=[ 9] get_value=[59]
pid=[53973] old_value=[94] new_value=[10] get_value=[87]
pid=[53970] old_value=[21] new_value=[11] get_value=[21]
pid=[53971] old_value=[34] new_value=[12] get_value=[82]
pid=[53972] old_value=[59] new_value=[13] get_value=[ 4]
pid=[53973] old_value=[87] new_value=[14] get_value=[70]

25

То, что вы говорите, это использовать метод следующим образом:

globvar = 5

def f():
    var = globvar
    print(var)

f()  # Prints 5

Но лучший способ - использовать глобальную переменную следующим образом:

globavar = 5
def f():
    global globvar
    print(globvar)
f()   #prints 5

Оба дают одинаковый результат.


25

Как оказалось, ответ всегда прост.

Вот небольшой пример модуля с простым способом показать его в mainопределении:

def five(enterAnumber,sumation):
    global helper
    helper  = enterAnumber + sumation

def isTheNumber():
    return helper

Вот как это показать в mainопределении:

import TestPy

def main():
    atest  = TestPy
    atest.five(5,8)
    print(atest.isTheNumber())

if __name__ == '__main__':
    main()

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


1
спасибо, я новичок в Python, но немного знаю Java. то, что ты сказал, сработало для меня. и написание global a <ENTER> внутри класса .. кажется, имеет больше смысла для меня, чем внутри функции, пишущей 'global a' .. Я заметил, что вы не можете сказать global a = 4
barlop

2
Это, наверное, самый простой, но очень полезный трюк с питоном для меня. Я называю этот модуль global_varsи инициализирую данные init_global_vars, которые вызываются в скрипте запуска. Затем я просто создаю метод доступа для каждого определенного глобального var. Я надеюсь, что смогу поднять это несколько раз! Спасибо, Питер!
swdev

1
Что если много глобальных переменных, и я не хочу перечислять их один за другим после глобального оператора?
jtlz2

23

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

Следующее:

var = "test"

def printGlobalText():
    global var #wWe are telling to explicitly use the global version
    var = "global from printGlobalText fun."
    print "var from printGlobalText: " + var

def printLocalText():
    #We are NOT telling to explicitly use the global version, so we are creating a local variable
    var = "local version from printLocalText fun"
    print "var from printLocalText: " + var

printGlobalText()
printLocalText()
"""
Output Result:
var from printGlobalText: global from printGlobalText fun.
var from printLocalText: local version from printLocalText
[Finished in 0.1s]
"""

3
«в каждой функции, которую вы хотите использовать», немного неверно, должно быть ближе к: «в каждой функции, где вы хотите обновить »
spazm

21

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

Если вам не нужно было явно указывать, когда идентификатор должен ссылаться на предопределенную глобальную переменную, вам, вероятно, придется явно указывать, когда вместо этого идентификатор является новой локальной переменной (например, с чем-то вроде команды 'var'). видел в JavaScript). Поскольку локальные переменные встречаются чаще, чем глобальные переменные в любой серьезной и нетривиальной системе, в большинстве случаев система Python имеет больше смысла.

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




14

В качестве дополнения используйте файл, содержащий все глобальные переменные, объявленные локально, а затем import as:

Файл initval.py :

Stocksin = 300
Prices = []

Файл getstocks.py :

import initval as iv

def getmystocks(): 
    iv.Stocksin = getstockcount()


def getmycharts():
    for ic in range(iv.Stocksin):

1
В чем преимущество перемещения глобальных переменных в другой файл? Это просто сгруппировать глобальные переменные в крошечный файл? А зачем использовать заявление import ... as ...? Почему не просто import ...?
olibre

1
Ах ... Я наконец-то понял преимущество: нет необходимости использовать ключевое слово global:-) => +1 :-) Пожалуйста, отредактируйте свой ответ, чтобы уточнить эти допросы, которые могут быть и у других людей. Приветствия
olibre

13

Запись в явные элементы глобального массива, по-видимому, не нуждается в глобальном объявлении, хотя запись в него «оптовым» действительно имеет это требование:

import numpy as np

hostValue = 3.14159
hostArray = np.array([2., 3.])
hostMatrix = np.array([[1.0, 0.0],[ 0.0, 1.0]])

def func1():
    global hostValue    # mandatory, else local.
    hostValue = 2.0

def func2():
    global hostValue    # mandatory, else UnboundLocalError.
    hostValue += 1.0

def func3():
    global hostArray    # mandatory, else local.
    hostArray = np.array([14., 15.])

def func4():            # no need for globals
    hostArray[0] = 123.4

def func5():            # no need for globals
    hostArray[1] += 1.0

def func6():            # no need for globals
    hostMatrix[1][1] = 12.

def func7():            # no need for globals
    hostMatrix[0][0] += 0.33

func1()
print "After func1(), hostValue = ", hostValue
func2()
print "After func2(), hostValue = ", hostValue
func3()
print "After func3(), hostArray = ", hostArray
func4()
print "After func4(), hostArray = ", hostArray
func5()
print "After func5(), hostArray = ", hostArray
func6()
print "After func6(), hostMatrix = \n", hostMatrix
func7()
print "After func7(), hostMatrix = \n", hostMatrix

7

Я добавляю это, поскольку я не видел это ни в одном из других ответов, и это могло бы быть полезным для кого-то, борющегося с чем-то подобным. globals()Функция возвращает изменяемый словарь глобальный символ , где вы можете «волшебно» данные сделать доступными для остальной части кода. Например:

from pickle import load
def loaditem(name):
    with open(r"C:\pickle\file\location"+"\{}.dat".format(name), "rb") as openfile:
        globals()[name] = load(openfile)
    return True

а также

from pickle import dump
def dumpfile(name):
    with open(name+".dat", "wb") as outfile:
        dump(globals()[name], outfile)
    return True

Просто позволит вам выгружать / загружать переменные из и в глобальное пространство имен. Супер удобно, без суеты, без суеты. Уверен, что это только Python 3.


3
globals()всегда возвращает глобальные переменные, доступные в локальном контексте, поэтому мутация здесь может не отражаться в другом модуле.
Киран Джонналагадда

6

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

В этом примере бегун использует max из файла config. Я хочу, чтобы мой тест изменил значение max, когда бегун его использует.

Основной / config.py

max = 15000

Основной / runner.py

from main import config
def check_threads():
    return max < thread_count 

Тесты / runner_test.py

from main import runner                # <----- 1. add file
from main.runner import check_threads
class RunnerTest(unittest):
   def test_threads(self):
       runner.max = 0                  # <----- 2. set global 
       check_threads()

1

Глобалы в порядке - за исключением многопроцессорной обработки

Глобальные связи в связи с многопроцессорностью на разных платформах / средах, таких как Windows / Mac OS с одной стороны и Linux с другой, являются проблематичными.

Я покажу вам это на простом примере, указывающем на проблему, с которой я столкнулся некоторое время назад.

Если вы хотите понять, почему в Windows / MacO и Linux все по-другому, вам нужно это знать, механизм по умолчанию для запуска нового процесса ...

  • Windows / MacOs - «порождение»
  • Linux - это «вилка»

Они отличаются в распределении памяти при инициализации ... (но я не буду вдаваться в подробности).

Давайте посмотрим на проблему / пример ...

import multiprocessing

counter = 0

def do(task_id):
    global counter
    counter +=1
    print(f'task {task_id}: counter = {counter}')

if __name__ == '__main__':

    pool = multiprocessing.Pool(processes=4)
    task_ids = list(range(4))
    pool.map(do, task_ids)

Windows

Если вы запустите это в Windows (и я полагаю, в MacOS тоже), вы получите следующий вывод ...

task 0: counter = 1
task 1: counter = 2
task 2: counter = 3
task 3: counter = 4

Linux

Если вы запустите это в Linux, вы получите следующее.

task 0: counter = 1
task 1: counter = 1
task 2: counter = 1
task 3: counter = 1
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.