Python функция глобальных переменных?

272

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

x = "somevalue"

def func_A ():
   global x
   # Do things to x
   return x

def func_B():
   x = func_A()
   # Do things
   return x

func_A()
func_B()

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

Акшат Шехар
источник
1
будьте осторожны и не предполагайте, что только потому, что в вашей функции назначена переменная, Python будет обрабатывать ссылки до назначения как такового. До первого присваивания, если вы использовали x, оно не будет ни глобальным, ни локальным. Вы получите печально известное исключение UnboundLocalError на вашем лице :)
osirisgothra

Ответы:

413

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

Например

global someVar
someVar = 55

Это изменило бы значение глобальной переменной на 55. В противном случае это просто присвоило бы 55 локальной переменной.

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

Левон
источник
2
В коде, который я дал, func_B выполняет действия (1) с глобальной копией x (полученной из func_A), (2) с локальной переменной x с тем же значением результата func_A, или (3) с локальная переменная x без значения и (в глазах компилятора) никакого отношения к «некоторому значению» или x в func_A?
Акшат Шекхар
xin func_B- это локальная переменная, которая получает свое значение из возвращаемого значения вызова func_A- так что я предполагаю, что это сделает его вашим (2)
Левон
Хорошо, скажем, x был случайной последовательностью некоторого вида, сгенерированной func_A (то есть, что func_A генерировал разные x при каждом запуске.) Запустившая программу, как написано, заставит func_b изменить x, отличный от того, который был изначально создан, когда func_a был называется? Если так, как я могу это исправить?
Акшат Шекхар
1
Да, если func_Aглобальная переменная изменяется во время каждого запуска и возвращается func_Bк использованию, то func_Bкаждый раз будет работать с измененным значением. Я не уверен насчет твоего "как это исправить". Вы можете принять наиболее полезный ответ на свой текущий / оригинальный вопрос, а затем рассмотреть вопрос об открытии другого вопроса о том, что выглядит как дополнительный вопрос.
Левон
1
На самом деле это зависит от того, что х. Если x неизменяемый, то x в func_B останется в нем, потому что он объявлен локально, даже если они имеют одинаковое значение. Это относится к кортежам, целым числам ... Если это, например, экземпляр списка, и вы делаете x.append("..."), это глобальная переменная x, которая изменяется, потому что локальная ссылается на глобальную.
jadkik94
111

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

Давайте посмотрим на модифицированную версию вашего псевдокода, чтобы увидеть, что происходит:

# Here, we're creating a variable 'x', in the __main__ scope.
x = 'None!'

def func_A():
  # The below declaration lets the function know that we
  #  mean the global 'x' when we refer to that variable, not
  #  any local one

  global x
  x = 'A'
  return x

def func_B():
  # Here, we are somewhat mislead.  We're actually involving two different
  #  variables named 'x'.  One is local to func_B, the other is global.

  # By calling func_A(), we do two things: we're reassigning the value
  #  of the GLOBAL x as part of func_A, and then taking that same value
  #  since it's returned by func_A, and assigning it to a LOCAL variable
  #  named 'x'.     
  x = func_A() # look at this as: x_local = func_A()

  # Here, we're assigning the value of 'B' to the LOCAL x.
  x = 'B' # look at this as: x_local = 'B'

  return x # look at this as: return x_local

Фактически, вы можете переписать все func_Bс именованной переменной, x_localи она будет работать одинаково.

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

def a():
  global foo
  foo = 'A'

def b():
  global foo
  foo = 'B'

b()
a()
print foo
# prints 'A' because a() was the last function to modify 'foo'.

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

x = 5

def access_only():
  return x
  # This returns whatever the global value of 'x' is

def modify():
  global x
  x = 'modified'
  return x
  # This function makes the global 'x' equal to 'modified', and then returns that value

def create_locally():
  x = 'local!'
  return x
  # This function creates a new local variable named 'x', and sets it as 'local',
  #  and returns that.  The global 'x' is untouched.

Обратите внимание, что разница между create_locallyи access_only- access_onlyзаключается в доступе к глобальному x, несмотря на то, что он не вызывается global, и даже если create_locallyон не используется global, он создает локальную копию, так как присваивает значение.

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

jdotjdot
источник
2
Я не думаю, что это очень сбивает с толку на практике, вы просто должны понять правила области видимости Python .
Кейси Кубалл
20

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

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

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

d = {}
l = []
o = type("object", (object,), {})()

def valid():     # these are all valid without declaring any names global!
   d[0] = 1      # changes what's in d, but d still points to the same object
   d[0] += 1     # ditto
   d.clear()     # ditto! d is now empty but it`s still the same object!
   l.append(0)   # l is still the same list but has an additional member
   o.test = 1    # creating new attribute on o, but o is still the same object
Kindall
источник
8

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

globVar = None    # initialize value of global variable

def func(param = globVar):   # use globVar as default value for param
    print 'param =', param, 'globVar =', globVar  # display values

def test():
    global globVar
    globVar = 42  # change value of global
    func()

test()
=========
output: param = None, globVar = 42

Я ожидал, что param будет иметь значение 42. Сюрприз. Python 2.7 оценивал значение globVar при первом анализе функции func. Изменение значения globVar не повлияло на значение по умолчанию, назначенное параметру. Задержка оценки, как в следующем, работала так, как мне было нужно.

def func(param = eval('globVar')):       # this seems to work
    print 'param =', param, 'globVar =', globVar  # display values

Или, если вы хотите быть в безопасности,

def func(param = None)):
    if param == None:
        param = globVar
    print 'param =', param, 'globVar =', globVar  # display values
SoloPilot
источник
Это напомнило мне о проблеме назначения пустого списка в качестве значения по умолчанию . И, как в примере, используйте isдля проверки, если что-то None, вместо обычного сравнения ==.
berna1111
6

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

var = 1
def global_var_change():
      global var
      var = "value changed"
global_var_change() #call the function for changes
print var

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

Noisy_Botnet
источник
2

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

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

Marcin
источник
2
Эта формулировка неудачна. В Python значение, присвоенное переменной, является ссылкой, поэтому оно технически правильно (и я не сомневаюсь, что вы это имели в виду), но многие читатели могут интерпретировать «изменить значение» как «изменить объект», что не является дело - xs.append(xs.pop(0))работает просто отлично без global xs.
@delnan Мой ответ тщательно сформулирован, но я уточню.
Марчин