Переменные переменных переменных вложенных функций Python

Я прочитал почти все другие вопросы по теме, но мой код все еще не работает.

Я думаю, что я пропускаю что-то в области переменных python.

  • Python: найти область многоугольника из координат xyz
  • Глобальные словари не нуждаются в ключевом слове global для их изменения?
  • Python - Почему эта переменная класса не определена в методе?
  • Почему доступ к переменной класса из класса требует «я» в Python?
  • Вычислить площадь заданных координат (x, y)
  • Ошибка области переменной Python
  • Вот мой код:

    PRICE_RANGES = { 64:(25, 0.35), 32:(13, 0.40), 16:(7, 0.45), 8:(4, 0.5) } def get_order_total(quantity): global PRICE_RANGES _total = 0 _i = PRICE_RANGES.iterkeys() def recurse(_i): try: key = _i.next() if quantity % key != quantity: _total += PRICE_RANGES[key][0] return recurse(_i) except StopIteration: return (key, quantity % key) res = recurse(_i) 

    И я получаю

    «глобальное имя« _total »не определено»

    Я знаю, что проблема _total с _total назначением, но я не могу понять, почему. Не следует ли recurse() доступ к переменным родительской функции?

    Может ли кто-нибудь объяснить мне, что мне не хватает в области переменных python?

  • Создание новых переменных в цикле с именами из списка в Python
  • «UnboundLocalError: локальная переменная« input », на которую ссылаются перед назначением»
  • Как проверить, является ли тип переменной строкой?
  • Глобальные словари не нуждаются в ключевом слове global для их изменения?
  • Импортировать переменные из другого файла?
  • Создание или назначение переменных из словаря в Python
  • 10 Solutions collect form web for “Переменные переменных переменных вложенных функций Python”

    Когда я запускаю код, я получаю эту ошибку:

     UnboundLocalError: local variable '_total' referenced before assignment 

    Эта проблема вызвана этой строкой:

     _total += PRICE_RANGES[key][0] 

    В документации по областям и пространствам имен указано следующее:

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

    Так как линия эффективно говорит:

     _total = _total + PRICE_RANGES[key][0] 

    он создает _total в пространстве имен recurse() . Поскольку _total затем новый и неназначенный, вы не можете использовать его в дополнении.

    Вот иллюстрация, которая доходит до сути ответа Дэвида.

     def outer(): a = 0 b = 1 def inner(): print a print b #b = 4 inner() outer() 

    С заявлением b = 4 закомментировано, этот код выводит 0 1 , что вы ожидаете.

    Но если вы раскомментируете эту строку, на строке print b вы получите ошибку

     UnboundLocalError: local variable 'b' referenced before assignment 

    Кажется загадочным, что присутствие b = 4 может как-то заставить b исчезнуть на линиях, которые предшествуют ему. Но текст Дэвида цитирует объясняет, почему: во время статического анализа интерпретатор определяет, что b назначается во inner , и поэтому он является локальной переменной inner . Линия печати пытается напечатать b в этой внутренней области до ее назначения.

    В Python 3 вы можете использовать nonlocal заявление для доступа к нелокальным, неглобальным областям.

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

     def sumsquares(x,y): def addsquare(n): sumsquares.total += n*n sumsquares.total = 0 addsquare(x) addsquare(y) return sumsquares.total 

    Конечно, этот атрибут принадлежит функции (defintion), а не вызову функции. Поэтому нужно помнить о потоках и рекурсии.

    Это вариант решения redman, но с использованием собственного пространства имен вместо массива для инкапсуляции переменной:

     def foo(): class local: counter = 0 def bar(): print(local.counter) local.counter += 1 bar() bar() bar() foo() foo() 

    Я не уверен, что использование объекта класса таким образом считается уродливым взломом или правильной методикой кодирования в сообществе python, но отлично работает в python 2.x и 3.x (проверено с 2.7.3 и 3.2.3 ). Я также не уверен в быстродействии этого решения.

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

     X=0 While X<20: Do something. .. X+=1 

    Я бы сделал это:

     X=[0] While X<20: Do something.... X[0]+=1 

    Таким образом, X никогда не является локальной переменной

     >>> def get_order_total(quantity): global PRICE_RANGES total = 0 _i = PRICE_RANGES.iterkeys() def recurse(_i): print locals() print globals() try: key = _i.next() if quantity % key != quantity: total += PRICE_RANGES[key][0] return recurse(_i) except StopIteration: return (key, quantity % key) print 'main function', locals(), globals() res = recurse(_i) >>> get_order_total(20) main function {'total': 0, 'recurse': <function recurse at 0xb76baed4>, '_i': <dictionary-keyiterator object at 0xb6473e64>, 'quantity': 20} {'__builtins__': <module '__builtin__' (built-in)>, 'PRICE_RANGES': {64: (25, 0.34999999999999998), 32: (13, 0.40000000000000002), 16: (7, 0.45000000000000001), 8: (4, 0.5)}, '__package__': None, 's': <function s at 0xb646adf4>, 'get_order_total': <function get_order_total at 0xb646ae64>, '__name__': '__main__', '__doc__': None} {'recurse': <function recurse at 0xb76baed4>, '_i': <dictionary-keyiterator object at 0xb6473e64>, 'quantity': 20} {'__builtins__': <module '__builtin__' (built-in)>, 'PRICE_RANGES': {64: (25, 0.34999999999999998), 32: (13, 0.40000000000000002), 16: (7, 0.45000000000000001), 8: (4, 0.5)}, '__package__': None, 's': <function s at 0xb646adf4>, 'get_order_total': <function get_order_total at 0xb646ae64>, '__name__': '__main__', '__doc__': None} {'recurse': <function recurse at 0xb76baed4>, '_i': <dictionary-keyiterator object at 0xb6473e64>, 'quantity': 20} {'__builtins__': <module '__builtin__' (built-in)>, 'PRICE_RANGES': {64: (25, 0.34999999999999998), 32: (13, 0.40000000000000002), 16: (7, 0.45000000000000001), 8: (4, 0.5)}, '__package__': None, 's': <function s at 0xb646adf4>, 'get_order_total': <function get_order_total at 0xb646ae64>, '__name__': '__main__', '__doc__': None} {'recurse': <function recurse at 0xb76baed4>, '_i': <dictionary-keyiterator object at 0xb6473e64>, 'quantity': 20} {'__builtins__': <module '__builtin__' (built-in)>, 'PRICE_RANGES': {64: (25, 0.34999999999999998), 32: (13, 0.40000000000000002), 16: (7, 0.45000000000000001), 8: (4, 0.5)}, '__package__': None, 's': <function s at 0xb646adf4>, 'get_order_total': <function get_order_total at 0xb646ae64>, '__name__': '__main__', '__doc__': None} Traceback (most recent call last): File "<pyshell#32>", line 1, in <module> get_order_total(20) File "<pyshell#31>", line 18, in get_order_total res = recurse(_i) File "<pyshell#31>", line 13, in recurse return recurse(_i) File "<pyshell#31>", line 13, in recurse return recurse(_i) File "<pyshell#31>", line 12, in recurse total += PRICE_RANGES[key][0] UnboundLocalError: local variable 'total' referenced before assignment >>> 

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

    Более с философской точки зрения, один ответ может быть «если у вас проблемы с пространством имен, дайте им пространство имен самого самого!»

    Предоставление этого в своем классе не только позволяет вам инкапсулировать проблему, но и упрощает тестирование, устраняет эти досадные глобальные переменные и уменьшает необходимость перекоса переменных между различными функциями верхнего уровня (несомненно, будет больше, чем просто get_order_total ) ,

    Сохраняя код OP, чтобы сосредоточиться на существенных изменениях,

     class Order(object): PRICE_RANGES = { 64:(25, 0.35), 32:(13, 0.40), 16:(7, 0.45), 8:(4, 0.5) } def __init__(self): self._total = None def get_order_total(self, quantity): self._total = 0 _i = self.PRICE_RANGES.iterkeys() def recurse(_i): try: key = _i.next() if quantity % key != quantity: self._total += self.PRICE_RANGES[key][0] return recurse(_i) except StopIteration: return (key, quantity % key) res = recurse(_i) #order = Order() #order.get_order_total(100) 

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

     def outer(): order = {'total': 0} def inner(): order['total'] += 42 inner() return order['total'] print outer() 

    Хотя я использовал подход на основе списка @ redman, он не является оптимальным с точки зрения удобочитаемости.

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

     def outer(recurse=2): if 0 == recurse: return def inner(): inner.attribute += 1 inner.attribute = 0 inner() inner() outer(recurse-1) inner() print "inner.attribute =", inner.attribute outer() outer() 

    Это печатает:

     inner.attribute = 3 inner.attribute = 3 inner.attribute = 3 inner.attribute = 3 

    Если I s/inner.attribute/outer.attribute/g , мы получаем:

     outer.attribute = 3 outer.attribute = 4 outer.attribute = 3 outer.attribute = 4 

    Поэтому, действительно, лучше сделать их атрибутами внутренней функции.

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

    Мой путь вокруг …

     def outer(): class Cont(object): var1 = None @classmethod def inner(cls, arg): cls.var1 = arg Cont.var1 = "Before" print Cont.var1 Cont.inner("After") print Cont.var1 outer() 
    Python - лучший язык программирования в мире.