본문 바로가기

개발하자

파이썬에서 글로벌이 아닌 외부(포착) 범위에 있는 변수를 수정할 수 있습니까?

반응형

파이썬에서 글로벌이 아닌 외부(포착) 범위에 있는 변수를 수정할 수 있습니까?

다음 예를 고려해 보십시오:

def A():
    b = 1
    def B():
        # I can access 'b' from here.
        print(b)
        # But can i modify 'b' here?
    B()
A()

함수의 코드의 경우 변수는 전역이 아닌 둘러싸는(외부) 범위에 있습니다. 내부에서 수정하려면 어떻게 해야 하나요? 직접 시도해보면 되고, 글로벌하지 않기 때문에 사용해도 문제가 해결되지 않습니다.


파이썬은 거의 모든 현대 언어와 마찬가지로 범위를 구현한다. 여기서의 기법은 호출자가 범위에 있지 않기 때문에 호출자가 또한 둘러싸는 함수가 아닌 한 호출자의 변수에 접근할 수 있게 한다. 이 문제에 대한 자세한 내용은 항목을 참조하십시오.




아니요, 적어도 이런 식으로는 안 됩니다.

"set operation"은 외부 이름을 포함하는 현재 스코프에서 새 이름을 생성하기 때문입니다.




에서 다음을 사용합니다:

문을 사용하면 나열된 식별자가 전역을 제외한 가장 가까운 범위에서 이전에 바인딩된 변수를 참조합니다. 바인딩의 기본 동작은 먼저 로컬 네임스페이스를 검색하는 것이기 때문에 이 기능이 중요합니다. 이 문을 사용하면 캡슐화된 코드가 전역(모듈) 범위 외에 로컬 범위 밖의 변수를 다시 바인딩할 수 있습니다.

def foo():
    a = 1
    def bar():
        nonlocal a
        a = 2
    bar()
    print(a)  # Output: 2

에서 변수를 재할당하는 대신 변경 가능한 개체(예: 목록 또는 dict)를 사용하고 값을 변환합니다:

def foo():
    a = []
    def bar():
        a.append(1)
    bar()
    bar()
    print a

foo()

출력:

[1, 1]



나는 파이썬이 조금 생소하지만, 이것에 대해 조금 읽었다. 나는 당신이 얻을 수 있는 최선의 방법이 당신의 외부 변수를 목록으로 감싸는 Java 해결책과 비슷하다고 믿는다.

def A():
   b = [1]
   def B():
      b[0] = 2
   B()
   print(b[0])

# The output is '2'

편집: 나는 이것이 아마도 파이썬 3 이전에 사실이었을 것이라고 생각한다. 그게 당신의 답인 것 같군요.




당신은 이걸 하고 싶지 않은 것 같아요. 주변 문맥에서 사물을 바꿀 수 있는 기능은 위험한데, 그 맥락은 함수에 대한 지식 없이 작성될 수 있기 때문이다.

클래스에서 B를 공용 메소드로 만들고 C를 개인 메소드로 만들거나(아마도 가장 좋은 방법) 목록과 같은 변경 가능한 유형을 사용하여 이를 명시적으로 C에 전달할 수 있습니다:

def A():
    x = [0]
    def B(var): 
        var[0] = 1
    B(x)
    print x

A()



사용할 수는 있지만 다음을 사용해야 합니다(글로벌 변수를 사용할 때 항상 좋은 솔루션은 아니지만 작동합니다):

def A():
    global b
    b = 1

    def B():
      global b
      print( b )
      b = 2

    B()
A()



파이썬 3에서 이 외부 공간이 전역 공간이 아닐 때 함수의 외부 공간의 속성이 있는지 == 모듈, 함수가 중첩 함수일 때의 속성이 있는지 모르겠다.

하지만 파이썬 2에는 그런 속성이 없는 것으로 알고 있습니다.

따라서 원하는 작업을 수행할 수 있는 유일한 가능성은 다음과 같습니다:

1) 다른 사람들이 말한 것처럼, 가변적인 물체를 사용하여

2)

def A() :
    b = 1
    print 'b before B() ==', b

    def B() :
        b = 10
        print 'b ==', b
        return b

    b = B()
    print 'b after B() ==', b

A()

결과

b before B() == 1
b == 10
b after B() == 10

.

노타

세드릭 줄리앙의 해결책에는 다음과 같은 단점이 있다:

def A() :
    global b # N1
    b = 1
    print '   b in function B before executing C() :', b

    def B() :
        global b # N2
        print '     b in function B before assigning b = 2 :', b
        b = 2
        print '     b in function B after  assigning b = 2 :', b

    B()
    print '   b in function A , after execution of B()', b

b = 450
print 'global b , before execution of A() :', b
A()
print 'global b , after execution of A() :', b

결과

global b , before execution of A() : 450
   b in function B before executing B() : 1
     b in function B before assigning b = 2 : 1
     b in function B after  assigning b = 2 : 2
   b in function A , after execution of B() 2
global b , after execution of A() : 2

의 실행 후 전역이 수정되었으며 이를 칭얼거리지 않을 수 있습니다

글로벌 네임스페이스에 식별자가 있는 개체가 있는 경우에만 해당됩니다




빈 클래스를 사용하여 임시 범위를 유지할 수 있습니다. 변이형 같은데 조금 더 예뻐요.

def outer_fn():
   class FnScope:
     b = 5
     c = 6
   def inner_fn():
      FnScope.b += 1
      FnScope.c += FnScope.b
   inner_fn()
   inner_fn()
   inner_fn()

그러면 다음과 같은 대화형 출력이 생성됩니다:

>>> outer_fn()
8 27
>>> fs = FnScope()
NameError: name 'FnScope' is not defined



이것을 훨씬 나중에 보는 사람들에게 더 안전하지만 무거운 해결책은. 변수를 매개 변수로 전달할 필요가 없습니다.

def outer():
    a = [1]
    def inner(a=a):
        a[0] += 1
    inner()
    return a[0]



자동으로 작동하는 간단한 대답

나는 이 특정한 문제를 해결하기 위해 파이썬 라이브러리를 만들었다. 언라이센스로 출시된 제품이니 원하는 대로 사용하세요. 여기에서 설치하거나 홈 페이지를 체크아웃할 수 있습니다

user@pc:home$ pip install seapie

from seapie import Seapie as seapie
def A():
    b = 1

    def B():
        seapie(1, "b=2")
        print(b)

    B()
A()

산출물

2

인수의 의미는 다음과 같습니다:

  • 첫 번째 인수는 실행 범위입니다. 0은 현지인, 1은 부모, 2는 조부모를 의미한다
  • 두 번째 인수는 지정된 범위에서 실행할 문자열 또는 코드 개체입니다
  • 에 대한 인수 없이도 호출할 수 있습니다

긴 대답

이것은 더 복잡하다. Seapie는 CPython api를 사용하여 콜 스택의 프레임을 편집함으로써 작동합니다. CPython은 사실상의 표준이기 때문에 대부분의 사람들은 그것에 대해 걱정할 필요가 없다.

만약 여러분이 이것을 읽고 있다면 아마도 가장 관심을 가질만한 마법 단어들은 다음과 같습니다:

frame = sys._getframe(1)          # 1 stands for previous frame
parent_locals = frame.f_locals    # true dictionary of parent locals
parent_globals = frame.f_globals  # true dictionary of parent globals

exec(codeblock, parent_globals, parent_locals)

ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),ctypes.c_int(1))
# the magic value 1 stands for ability to introduce new variables. 0 for update-only

후자는 업데이트를 강제로 로컬 범위로 전달합니다. 그러나 로컬 범위는 글로벌 범위와 다르게 최적화되어 있으므로 새로운 개체를 추론하는 데는 어떤 식으로든 초기화되지 않은 경우 직접 호출하려고 할 때 몇 가지 문제가 있습니다. 나는 github 페이지에서 이러한 문제를 피하는 몇 가지 방법을 복사할 것이다

  • 사전에 개체 할당, 가져오기 및 정의
  • 미리 개체에 자리 표시자 할당
  • 기호 테이블을 업데이트하려면 주 프로그램에서 개체를 자신에게 재할당하십시오. x = locals(["x"])
  • 최적화를 피하기 위해 직접 호출하는 대신 주 프로그램에서 exec()을 사용합니다. x do를 호출하는 대신 exec("x")

사용하는 것이 원하는 것이 아니라고 생각되는 경우 로컬 사전()을 업데이트하여 동작을 에뮬레이트할 수 있습니다. 나는 다음의 예를 복사할 것이다

import sys
import ctypes

def hack():
    # Get the frame object of the caller
    frame = sys._getframe(1)
    frame.f_locals['x'] = "hack!"
    # Force an update of locals array from locals dict
    ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),
                                          ctypes.c_int(0))

def func():
    x = 1
    hack()
    print(x)

func()

출력:

hack!

반응형