파이썬에서 전역이 아닌 외부 범위에있는 변수를 수정할 수 있습니까?
다음 코드가 주어집니다.
def A() :
b = 1
def B() :
# I can access 'b' from here.
print( b )
# But can i modify 'b' here? 'global' and assignment will not work.
B()
A()
B()
함수 변수 의 코드는 b
외부 범위에 있지만 전역 범위에는 없습니다. 함수 b
내에서 변수 를 수정할 수 B()
있습니까? 확실히 여기에서 읽을 수 print()
있지만 수정하는 방법은 무엇입니까?
Python 3.x에는 nonlocal
키워드가 있습니다. 나는 이것이 당신이 원하는 것을한다고 생각하지만 파이썬 2 또는 3을 실행 중인지 확실하지 않습니다.
nonlocal 문은 나열된 식별자가 가장 가까운 둘러싸는 범위에서 이전에 바인딩 된 변수를 참조하도록합니다. 바인딩의 기본 동작은 먼저 로컬 네임 스페이스를 검색하기 때문에 중요합니다. 이 명령문을 사용하면 캡슐화 된 코드가 전역 (모듈) 범위 외에 로컬 범위 외부의 변수를 리 바인드 할 수 있습니다.
파이썬 2의 경우 일반적으로 변경 가능한 객체 (목록 또는 사전과 같은)를 사용하고 재 할당하는 대신 값을 변경합니다.
예:
def foo():
a = []
def bar():
a.append(1)
bar()
bar()
print a
foo()
출력 :
[1, 1]
빈 클래스를 사용하여 임시 범위를 유지할 수 있습니다. 그것은 가변적이지만 조금 더 예쁘다.
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
저는 Python을 처음 접했지만 이것에 대해 조금 읽었습니다. 나는 당신이 얻을 수있는 최선의 방법이 외부 변수를 목록으로 감싸는 Java 해결 방법과 유사하다고 생각합니다.
def A():
b = [1]
def B():
b[0] = 2
B()
print(b[0])
# The output is '2'
편집 : 나는 이것이 아마도 파이썬 3 이전에 사실이라고 생각합니다 nonlocal
. 당신의 대답은 같습니다 .
적어도 이런 식으로는 할 수 없습니다.
"set operation"은 현재 범위에 외부 이름을 포함하는 새 이름을 생성하기 때문입니다.
I don't think you should want to do this. Functions that can alter things in their enclosing context are dangerous, as that context may be written without the knowledge of the function.
You could make it explicit, either by making B a public method and C a private method in a class (the best way probably); or by using a mutable type such as a list and passing it explicitly to C:
def A():
x = [0]
def B(var):
var[0] = 1
B(x)
print x
A()
For anyone looking at this much later on a safer but heavier workaround is. Without a need to pass variables as parameters.
def outer():
a = [1]
def inner(a=a):
a[0] += 1
inner()
return a[0]
You can, but you'll have to use the global statment (not a really good solution as always when using global variables, but it works):
def A():
global b
b = 1
def B():
global b
print( b )
b = 2
B()
A()
I don't know if there is an attribute of a function that gives the __dict__
of the outer space of the function when this outer space isn't the global space == the module, which is the case when the function is a nested function, in Python 3.
But in Python 2, as far as I know, there isn't such an attribute.
So the only possibilities to do what you want is:
1) using a mutable object, as said by others
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()
result
b before B() == 1
b == 10
b after B() == 10
.
Nota
The solution of Cédric Julien has a drawback:
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
result
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
The global b after execution of A()
has been modified and it may be not whished so
That's the case only if there is an object with identifier b in the global namespace
The short answer that will just work automagically
I created a python library for solving this specific problem. It is released under the unlisence so use it however you wish. You can install it with pip install seapie
or check out the home page here https://github.com/hirsimaki-markus/SEAPIE
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()
outputs
2
the arguments have following meaning:
- The first argument is execution scope. 0 would mean local
B()
, 1 means parentA()
and 2 would mean grandparent<module>
aka global - The second argument is a string or code object you want to execute in the given scope
- You can also call it without arguments for interactive shell inside your program
The long answer
This is more complicated. Seapie works by editing the frames in call stack using CPython api. CPython is the de facto standard so most people don't have to worry about it.
The magic words you are probably most likely interesed in if you are reading this are the following:
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
The latter will force updates to pass into local scope. local scopes are however optimized differently than global scope so intoducing new objects has some problems when you try to call them directly if they are not initialized in any way. I will copy few ways to circumvent these problems from the github page
- Assingn, import and define your objects beforehand
- Assingn placeholder to your objects beforehand
- Reassign object to itself in main program to update symbol table: x = locals()["x"]
- Use exec() in main program instead of directly calling to avoid optimization. Instead of calling x do: exec("x")
If you are feeling that using exec()
is not something you want to go with you can emulate the behaviour by updating the the true local dictionary (not the one returned by locals()). I will copy an example from https://faster-cpython.readthedocs.io/mutable.html
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()
Output:
hack!
'IT박스' 카테고리의 다른 글
쿼리 매개 변수와 행렬 매개 변수를 사용하는 경우 (0) | 2020.09.06 |
---|---|
asm.js와 WebAssembly의 차이점은 무엇입니까? (0) | 2020.09.06 |
Excel 2010에서 "양식 컨트롤"과 "ActiveX 컨트롤"의 차이점은 무엇입니까? (0) | 2020.09.06 |
Django : 외래 키 충돌에 대한 역방향 접근 자 (0) | 2020.09.06 |
투명한 배경이 기기에 검은 색 배경을 보여주는 iOS 앱 아이콘 (0) | 2020.09.06 |