programing

Python 중첩 함수 변수 범위 지정

lovejava 2023. 7. 8. 10:25

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'이(가) 정의되지 않았습니다."

가 나는문제있것압니다을다는가▁the다▁on▁the에 있다는 것을 알고 있습니다._total왜 그런지 이해할 수가 없어요해선 안된다recurse()상위 기능의 변수에 액세스할 수 있습니까?

누가 파이썬 가변 범위에 대해 제가 빠진 것을 설명해주실 수 있나요?

Python 3에서는 을 사용하여 로컬이 아닌 전역이 아닌 범위에 액세스할 수 있습니다.

nonlocal문을 사용하면 변수 정의가 이전에 생성된 변수와 가장 가까운 범위에 바인딩됩니다.다음은 몇 가지 예입니다.

def sum_list_items(_list):
    total = 0

    def do_the_sum(_list):
        for i in _list:
            total += i

    do_the_sum(_list)

    return total

sum_list_items([1, 2, 3])

는 다음합니다.UnboundLocalError: local variable 'total' referenced before assignment

용사를 합니다.nonlocal코드를 작동시킬 수 있습니다.

def sum_list_items(_list):
    total = 0

    def do_the_sum(_list):

        # Define the total variable as non-local, causing it to bind
        # to the nearest non-global variable also called total.
        nonlocal total

        for i in _list:
            total += i

    do_the_sum(_list)

    return total

sum_list_items([1, 2, 3])

하지만 "가장 가까운"은 무엇을 의미할까요?다음은 또 다른 예입니다.

def sum_list_items(_list):

    total = 0

    def do_the_sum(_list):

        # The nonlocal total binds to this variable.
        total = 0

        def do_core_computations(_list):

            # Define the total variable as non-local, causing it to bind
            # to the nearest non-global variable also called total.
            nonlocal total

            for i in _list:
                total += i

        do_core_computations(_list)

    do_the_sum(_list)

    return total

sum_list_items([1, 2, 3])

예에서, 위의예에서,,total는 부에정 변바니다됩인딩수 .do_the_sum 수고정에서 정의된 .sum_list_items함수, 그래서 코드가 반환됩니다.0할 수 . if 다 과 같 수 있 수 니 습 다 할 행 을 음 중 첩 이 중 은 ▁such ▁note 니 다 습 있 ▁nesting ▁if ▁double 다 수 : ▁as ▁that ▁this ▁ittotal 선됨으로 선언됩니다.nonlocaldo_the_sum위의 예는 예상대로 작동할 것입니다.

def sum_list_items(_list):

    # The nonlocal total binds to this variable.
    total = 0

    def do_the_sum(_list):

        def do_core_computations(_list):

            # Define the total variable as non-local, causing it to bind
            # to the nearest non-global variable also called total.
            nonlocal total

            for i in _list:
                total += i

        do_core_computations(_list)

    do_the_sum(_list)

    return total

sum_list_items([1, 2, 3])

위의 예에서 비로컬 할당은 위치를 찾기 전에 두 개의 레벨을 통과합니다.totalsum_list_items.

여기 데이비드의 대답의 핵심을 이루는 삽화가 있습니다.

def outer():
    a = 0
    b = 1

    def inner():
        print a
        print b
        #b = 4

    inner()

outer()

명서와함께라는 .b = 4 out,이는 " 트코아웃출다, 코는니합력드이멘다▁outputs니▁commented출"를 출력합니다.0 1당신이 예상했던 바로 그것.

하지만 만약 당신이 그 대사를 언급하지 않는다면, 그 대사에.print b당신은 오류를 이해합니다.

UnboundLocalError: local variable 'b' referenced before assignment

가 존재한다는 것은 불가사의해 보입니다.b = 4든 어떻든만수있것을들게▁somehow것▁might▁make있을수어.b그 앞의 행에서 사라집니다.그러나 David가 인용한 텍스트는 그 이유를 설명합니다: 정적 분석 중에, 통역사는 b가 다음에 할당된 것을 결정합니다.inner 따서 그은 변지수다니입의 국소 입니다.inner▁the다▁attempts▁the▁print▁to를 인쇄하려고 합니다.b할당되기 전에 내부 범위에서.

코드를 실행하면 다음 오류가 발생합니다.

UnboundLocalError: local variable '_total' referenced before assignment

이 문제는 다음 행으로 인해 발생합니다.

_total += PRICE_RANGES[key][0]

범위네임스페이스에 대한 설명서에는 다음과 같이 나와 있습니다.

Python의 특별한 특징은 그렇지 않은 경우global이 적용됩니다. 이름에 대한 할당은 항상 가장 안쪽 범위에 들어갑니다.할당은 데이터를 복사하지 않고 이름만 개체에 바인딩합니다.

그래서 그 선이 효과적으로 말하고 있기 때문에:

_total = _total + PRICE_RANGES[key][0]

그것은 창조합니다._total로의 recurse()부터.부터_total그러면 새 항목이고 할당되지 않은 항목입니다. 추가에 사용할 수 없습니다.

특수 개체나 맵 또는 배열을 선언하는 대신 함수 속성을 사용할 수도 있습니다.이를 통해 변수의 범위를 명확하게 지정할 수 있습니다.

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

  sumsquares.total = 0
  addsquare(x)
  addsquare(y)
  return sumsquares.total

물론 이 속성은 함수 호출이 아니라 함수(정의)에 속합니다.따라서 스레드화와 재귀를 염두에 두어야 합니다.

이것은 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으로 테스트됨).또한 이 솔루션의 런타임 효율성에 대해서도 잘 모르겠습니다.

철학적 관점에서는 "이름 공간에 문제가 있는 경우 고유한 이름 공간을 지정하십시오!"라고 대답할 수 있습니다.

할 수 쉽게 수행할 수 있고, 할 수 , 다양한 수 있습니다(할 여지 없이▁just▁than▁around▁providing▁(dou▁be'▁more▁not▁therell▁in수btless있,▁it며▁own▁its▁various▁the▁variables수할으▁between▁class자행▁top-쉽als더게체▁but▁functions▁easier▁to▁and▁shovel▁problem▁you▁to▁reduceslevelky,▁globulate 성가신 글로벌 요소를 제거하고 다양한 최상위 기능 간에 변수를 삽입할 필요성을 줄일 수 있습니다(의심할 여지 없이 그 이상의 기능이 있을 것입니다).get_order_total).

근본적인 변화에 초점을 맞추기 위해 작전 코드를 보존하는 것,

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

내가 만약에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()
>>> 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의 로컬 범위에만 정의되어 있기 때문입니다.

언급URL : https://stackoverflow.com/questions/5218895/python-nested-functions-variable-scoping