중첩함수(Nested function)와 비지역변수
함수는 일급객체(First Class Object)이므로 다음과 같이 사용되는 다른 함수에 인수로 전달 할 수 있습니다.
def expNum(x, y): return(x**y) def addExp(func, x, y): return(func(x, y)+x+y) x, y= 2, 3 addExp(expNum, x, y)
13
위 코드의 addExp()는 다른 함수를 인수로 사용하는 함수입니다. 다음 코드에서 작성한 함수 addExp1()은 위에서 인수로 전달받는 함수를 함수 내부에 포함하는 방식으로 작성한 것으로 같은 결과를 나타냅니다.
def addExp1(x, y): re=expNum(x, y) return(re+x+y) addExp1(x, y)
13
위 코드에서 함수 addExp1()와 같이 함수 내부에 포함된 함수를 중첩함수(nested function)라고 합니다.
변수는 선언되는 위치에 따라 전역변수(global variable)와 지역변수(local variable)로 구분합니다. 이러한 구분은 각 변수의 작동영역이 제한되기 때문입니다. 중첩함수 내부에 선언된 변수는 그 내부에서만 작동합니다. 이 경우 이 변수가 지역변수가 되지만 그 외 부분에서 선언된 변수는 모두 전역변수가 될수는 없습니다. 이렇게 중접함수를 포함하는 계층적 구조를 가지는 함수의 경우 그림 1과 같이 구분합니다. 즉, 가장 내부에 포함된 함수의 변수는 지역변수가 되며 그 변수의 영역의 바깥 부분에 존재하는 변수는 비지역변수(nonlocal variable)가 됩니다. 또한 블럭에 상관없이 모든 부분에서 사용가능한 변수를 전역변수라고 할 수 있습니다. 그러므로 비지역변수는 지역변수와 전역변수 사이에 위치하는 모든 변수를 통칭합니다.
다음 코드에서 함수 greeting()는 중첩 함수인 printGr()을 포함하고 있습니다.
x="Good" # 전역변수 def greeting(name): # name : 비지역변수 y="morning" # 비지역변수 def printGr(): # 중첩함수 name="Han" # 지역변수 return(f"{x} {y}, {name}") return(printGr())
greeting("Kim")
'Good morning, Han'
위 코드에서 외곽함수인 greeting()에 인수로 전달한 name = "kim"과 중첩함수인 printGr() 내부에서 선언된 지역변수 name = "Han"의 변수이름이 같지만 외곽함수의 실행은 "Han"을 반환합니다. 이것은 변수의 작동영역의 구분과 우선순위 때문입니다. 다시말하면, 변수의 작동영역은 포함된 블럭내로 한정되며 블럭 내에 있는 변수의 실행이 우선됩니다.
위 함수의 실행에서 각 변수의 적용 절차는 다음과 같습니다.
- 함수 greeting() 호출 : 인수 name이 전달됩니다.
- greeting()의 실행으로 printGr()함수 호출
- printGr()함수는 변수 name, x, y를 사용
- name은 이 함수 블럭에 선언되어 있으므로 외부에 존재하는 동일이름의 변수는 무시됩니다.
- 같은 블럭에 속한 변수를 local variable(지역변수)
- 그 블럭 외에 존재하는 변수를 non-local variable(비지역 변수)이라고 합니다.
- 변수 x, y: 그 두 변수는 함수 printGr()에 포함되어 있지 않습니다.
- greeting()함수내의 y값을 사용합니다.
- greeting()함수 외부에 존재하는 변수 x를 사용합니다.
- x는 greeting() 외부에 존재하므로 그 함수 외의 다른 모든 코드에서도 사용할 수 있습니다. 이러한 변수를 global variable(전역변수)라고 합니다.
위 코드에서 함수 printGr() 내에 존재하지 않은 x, y 값은 외부로부터 호출하여 사용하였습니다. 그러나 동일한 블럭에서 선언되지 않은 변수의 사용은 읽기만 가능하고 수정할 수 없습니다. 즉, 변수 값은 선언한 위치에서만 수정이 가능합니다.
다음 함수는 외곽함수의 변수를 중첩함수에서 수정하는 것으로 에러를 발생합니다.
def greeting(name): x="hello" def greeting2(): x=name+', '+x print(x) greeting2() greeting("Oh")
UnboundLocalError: local variable 'x' referenced before assignment
위 코드에서 중첩함수의 변수 x를 새로운 변수 y로 변형하면 작동합니다.
def greeting(name): x="hello" def greeting2(): y=name+', '+x print(y) greeting2() greeting("Oh")
Oh, hello
기사 "전역변수와 지역변수" 소개한 global 문은 전역변수를 블럭내에서 수정할 수 있게 합니다. 이와 유사하게 nonlocal 문
을 적용하여 식 1과 같이 비지역변수를 다시 선언하여 블럭내에서 수정할 수 있게 합니다.
nonlocal 비지역변수이름 | (식 1) |
def greeting(name): x="hello" def greeting2(): nonlocal x x=name+', '+x print(x) greeting2() greeting("Oh")
Oh, hello
변수를 전역, 비지역, 그리고 지역으로 구분하는 것은 복잡한 코드의 계층적 구조에서 변수의 관리를 용이하게 합니다. 그러나 비지역 변수를 특정한 블럭(지역)에서 수정한다면 그 변수를 사용하는 다른 블럭의 결과는 왜곡될 수 있습니다. 일반적으로 비지역 변수는 하위 지역에서는 상수로 고려될 수 있기 때문입니다. nonlocal 문은 비지역 변수의 이러한 특성을 잃게하는 것으로 신중하게 사용되어야 합니다.
댓글
댓글 쓰기