Может кто-нибудь помочь мне понять область видимости переменных Python? - PullRequest
1 голос
/ 13 августа 2011

Я написал тестовую программу, которая выглядела так:

#!/usr/bin/python

def incrementc():
    c = c + 1

def main():
    c = 5
    incrementc()

main()

print c

Я бы подумал, что, поскольку я вызвал incrementc в теле main, все переменные из main будут переданы в incrementc. Но когда я запускаю эту программу, я получаю

Traceback (most recent call last):
  File "test.py", line 10, in <module>
    main()
  File "test.py", line 8, in main
    incrementc()
  File "test.py", line 4, in incrementc
    c = c + 1
UnboundLocalError: local variable 'c' referenced before assignment

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

Спасибо!

Ответы [ 4 ]

6 голосов
/ 13 августа 2011

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

Чтобы выполнить то, что вы хотите, вы должны использовать глобальную переменную или, что лучше, передать c в качестве параметра. Теперь, поскольку примитивы в Python являются неизменяемыми, передача целого числа не может быть изменена (по сути, он передается по значению), поэтому вам нужно будет упаковать его в контейнер, например список. Как это:

def increment(l):
    l[0] = l[0] + 1

def main():
    c = [5]
    increment(c)
    print c[0]

main()

Или еще проще:

def increment(l):
    return l + 1

def main():
    c = 5
    print increment(c)

main()

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

3 голосов
/ 13 августа 2011

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

Итак, чтобы заставить это работать так, как вы думаете, вам нужно использовать два global утверждения:

#!/usr/bin/python
def incrementc():
    global c
    c = c + 1
def main():
    global c
    c = 5
    incrementc()
main()
print c

В противном случае вы говорите о локальной переменной с именем c в обеих ситуациях.

Обычный способ решения этой проблемы, однако, не включает global с.

#!/usr/bin/python
def incrementc(c):
    c = c + 1
    return c
def main():
    c = 5
    c = incrementc(c)
    return c
c = main()
print c

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

class Foo:
    def __init__(self, c):
        self.c = c
        self.incrementc()
    def incrementc(self):
        self.c = self.c + 1


foo = Foo(5)
print foo.c
2 голосов
/ 13 августа 2011

Переменная c не проходит, потому что вы не передаете ссылку на c функции incrementc.

Здесь вы видите 3 области видимости, глобальную область видимости и области внутри функций main и incrementc. В основном вы правильно определили переменную c, но приращение c не знает об этом - поэтому попытка приращения приведет к провалу. Даже если эти две функции завершатся успешно, попытка напечатать c потерпит неудачу в глобальной области видимости, поскольку она не знает о c, который вы определили в main.

У вас есть несколько вариантов. Один из способов сделать это:

def incrementc(c):
    c = c + 1
    return c

def main():
    c = 5
    c = incrementc(c)
    return c

c = main()
print c

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

def increment(z):
    z = z + 1
    return z

def main():
    bar = 5
    bar = increment(bar)
    return bar

foo = main()
print foo

Другим вариантом, который многим, вероятно, не понравится (по уважительной причине), является использование глобальных переменных. В этом случае:

def incrementc():
    global c # indicate intention to write to this global, not just read it
    c = c + 1 

def main():
    global c # declares c in global space
    c = 5
    incrementc()

main()
print c

Любая функция, в которой вы надеетесь изменить глобальный экземпляр c, вам нужно сообщить об этой функции. Итак, вы заявляете, «глобальный с». Вы можете ПРОЧИТАТЬ из глобального без этого. Это позволит (в некоторой степени) избежать ошибки и непреднамеренно перезаписать значение в глобальном пространстве аналогичным именем, если вы решите использовать его в локальном пространстве функции.

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

0 голосов
/ 13 августа 2011

Глобальные переменные плохие.

Так же, как друзья и враги.Держите своих друзей близко, но держите своих врагов еще ближе.

Функция выполняет последнюю локальную переменную c, присваивая значение 5 Затем вызывается функция inc..C.C из main теперь находится вне области видимости, поэтому вы пытаетесь использовать значение c, которое находится за пределами области действия - следовательно, ошибка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...