-
TIL. 29 전역 변수, 지역변수, 클로저 알아보기TIL 2020. 11. 6. 21:56728x90
## 클로저를 알기 위해서는 먼저, 함수의 사용 범위를 먼저 알아보자.
## 전역 함수와 지역 함수
## 전역 변수 (global variable) == 위처럼 함수를 포함하여 스크립트 전체에서 접글 할 수 있는 변수를 전역 변수라 한다.
# 특히, 전역 번수에 접근 할 수 있는 범위를 전역 범위 (global scope)라고 한다.
x = 10 # 전역 변수 def foo(): print(x) # 전역 변수 출력 foo() print(x) # 전역 변수 출력 :: 10
## foo 함수에 x라는 변수가 업지만, 전역 변수로 x가 있으므로 foo 함수에서도 x 변수를 사용할 수 있다.
## 지역 변수 (local variable) == 지역 변수 영역이 정해진 변수로 변수를 만든 함수 안에서만 접근이 가능하며
함수 바깥에서는 접근 할 수없다.
# 특히, 지역 변수에 접근 할 수 있는 범위를 지역 범위 (local scope)라고 한다.
def foo(): x = 10 # foo의 지역 변수 print(x) # foo의 지역 변수 출력 foo() :: 10 print(x) # error . foo의 지역 변수는 출력할 수 없음
# 위의 print(x)는 error ==> 지역 변수로만 존재하는 x에 접근 할 수 없다.
## print 함수가 지역 범위에 없기 때문이다.
## 전역 변수 x 와 지역 변수 x는 같을까?
x = 10 # 전역 변수 def foo(): x = 20 # x는 foo의 지역 변수 print(x) # foo의 지역 변수 출력 foo() :: 20 print(x) # 전역 변수 출력 :: 10
## foo() 출력값은 지역 변수 x 값인 20을 출력하고, print(x)는 전역 변수 x값인 10을 출력한다.
## 여기서 전역 변수 x 와 지역 변수 x 는 "이름만 같을 뿐 서로 다른 변수로, 각 값은 서로 다른 메모리에 저장된다."
## 함수 안에서 전역 변수 변경하기
## 함수 안에서 전역 변수의 값을 변경할 수 있다.
# global 전역변수
x = 10 # 전역 변수 def foo(): global x # 전역 변수 x를 사용하겠다고 설정 x = 20 # x는 전역 변수 , x = 20이 다시 할당되는 걸로 이해 print(x) # 전역 변수 출력 foo() :: 20 print(x) # 전역 변수 출력 :: 20
## global x 라는 뜻은 여기서 부터 전역 변수 x를 사용하겠다는 뜻으로 해석해도 된다.
## 따라서, global x 를 작성한 이후의 변수 x는 전역 변수 x를 의미한다.
## 전역 변수가 없을때의 global
# 전역 변수 x가 없는 상태 def foo(): global x # x를 전역 변수로 만듦 x = 20 # x는 전역 변수 print(x) # 전역 변수 출력 foo() :: 20 print(x) # 전역 변수 출력 :: 20
## 전역 변수가 아무것도 없을때 global 를 사용할 경우 해당 변수는 전역 변수가 된다.
# x는 전역 변수가 된다.
## 함수 안에서 함수 만들기
## def 를 한 함수안에서 계속 사용하여 함수 안에 함수를 만들 수 있다.
# def 함수이름1():
# 코드
# def 함수이름2():
# 코드
def print_hello(): hello = 'Hello, world!' def print_message(): print(hello) print_message() print_hello() :: 'Hello, world!'
## 함수 호출시 작동 순서를 본다면 // 1줄, 2줄, 5줄, 3줄, 4줄 로 표현 할 수 있다.
## 위 함수에서 보면 print_messge 함수 입장에서는 자신이 사용할 수 있는 변수 hello가 없다.
## 그런데도 error가 나지 않는 이유는 "바깥쪽 함수의 지역 변수는 그 안에 속한 모든 함수에서 접근할 수 있는 특징"때문이다.
## 바깥쪽 함수의 지연 변수를 안쪽 함수에서 변경하기
def A(): x = 10 # A의 지역 변수 x def B(): x = 20 # x에 20 할당 B() print(x) # A의 지역 변수 x 출력 A() :: 10
## 위 함수에서 중요 포인트는 print가 어떤 함수에 위치해 있는가 이다.
## 얼핏 보기에는 B 함수의 지역 변수 20을 출력해야할 것 같지만,
print 함수가 A 함수에 위치해 있기 때문에 A의 지역변수 10을 출력한다.
## 파이썬에서는 함수 에서 변수를 만들면 그 변수는 항상 해당 함수의 지역 변수가 된다.
def A(): x = 10 # A의 지역 변수 x def B(): x = 20 # x에 20 할당 print(x) B() A() :: 20
## 위와 같이 print 함수를 B 함수에 위치시키면 B의 지역변수인 20을 출력한다.
## 현재 함수에서 바깥쪽 함수의 지역 변수 변경하기
# nonlocal 지역변수
def A(): x = 10 # A의 지역 변수 x def B(): nonlocal x # 현재 함수의 바깥쪽에 있는 지역 변수 사용 x = 20 # A의 지역 변수 x에 20 할당 B() print(x) # A의 지역 변수 x 출력 A() :: 20
## nonlocal x 를 이용해 현재 함수인 B 함수에서 바깥쪽에 있는 (A함수)의 지역 변수를 변경 할 수 있다.
## global 과 비슷하게 nonlocal x라는 뜻은 여기서 부터 바깥쪽 함수의 지역 변수 x를 사용하겠다는 뜻으로 해석
## 따라서 nonlocal x 를 작성한 이후의 변수 x는 바깥쪽 함수의 지역 변수 x를 의미힌다.
## nonlocal은 현재 함수의 지역 변수가 아니라는 뜻이며 바깥쪽 함수의 지역 변수를 사용한다.
## nonlocal 의 변수를 찾는 순서
def A(): x = 10 y = 100 def B(): x = 20 def C(): nonlocal x # B 의 지역 함수 x = 20 nonlocal y # A 의 지역 한수 y = 100 x = x + 30 y = y + 300 print(x) print(y) C() B() A() :: 50 400
## nonlocal 은 바깥쪽 지역함수를 찾을때 가장 가까운 함수에서의 지역 함수 값을 찾는다.
## 위 nonlocal y --> B의 지역 함수 y가 있었다면 B 함수의 지역 함수 y를 사용하였을것이다.
## 실무에서는 이렇게 여러 단계로 함수를 만들 일은 거의 없다.
## 그리고 함수마다 이름이 같은 변수를 사용하기 보다는 변수 이름을 다르게 짓는 것이 훨씬 좋다.
## global로 전역 변수 사용하기
x = 1 def A(): x = 10 def B(): x = 20 def C(): global x x = x + 30 print(x) C() B() A() :: 31
## 함수가 몇개가있던 전역 변수 x =1 을 사용한다.
## 파이썬에서 global을 제공 "하지만" 함수에서 값을 주고받을 때는 매개변수와 반환값을 사용하는 것이 좋다.
## 특히 전역 변수는 코드가 복잡해졌을 때 변수의 값을 어디서 바꾸는지 파악하기 힘들다.
## 따라서 전역 변수는 가급적이면 사용하지 않는 것을 권장한다.
## 클로저 사용하기
## 함수를 클로저 형태로 만드는 방법
def calc(): a = 3 b = 5 def mul_add(x): return a * x + b # 함수 바깥쪽에 있는 지역 변수 a, b를 사용하여 계산 return mul_add # mul_add 함수를 반환 c = calc() print(c(1), c(2), c(3), c(4), c(5)) :: 8 11 14 17 20
# 함수 바깥쪽에 있는 지역 변수 a, b를 사용하여 a * x + b를 계산하는 함수 mul_add를 만든 뒤에 함수 mul_add 자체를 반환.
## 함수 mul_add를 만든 뒤, 이 함수를 바로 호출하지 않고 return으로 함수 자체를 반환
## (함수를 반환할 때는 함수 이름만 반환해야 하며 ( )(괄호)를 붙이면 안 됩니다).
c = calc() print(c(1), c(2), c(3), c(4), c(5)) :: 8 11 14 17 20
## 위와 같이 calc 함수를 호출한뒤 반환값을 c에 할당한다.
## calc에서 mul_add 를 반환하였으므로 c에는 mul_add가 들어가게 된다.
## 따라서 c(인수) 형태로 호출해보면 mul_add의 a*xb의 계산식을 사용할 수 있게된다.
## 또한, 자세히 보면 함수 c = calc() 에서 calc 함수는 1번 사용하고 끝났음에도 불구하고
## c 는 calc의 지역함수 a, b 를 계속 사용하는 것을 볼 수 있다.
## 이렇게 함수를 둘러싼 환경(지역 변수, 코드 등)을 계속 유지하다가,
## 함수를 호출할 때 다시 꺼내서 사용하는 함수를 클로저(closure)라고 한다.
## 여기서는 c에 저장된 함수가 클로저이다.
## 클로저를 사용하는 이유
# 1. 지역 변수와 코드를 묶어서 사용하고 싶을 때 활용한다
# 2. 또한, 클로저에 속한 지역 변수는 바깥에서 직접 접근할 수 없으므로 데이터를 숨기고 싶을 때 활용.
추가로 클로저를 사용하면 프로그램의 흐름을 변수에 저장 할 수 있다
## lambda로 클로저 만들기
def calc(): a = 3 b = 5 return lambda x: a * x + b # 람다 표현식을 반환 c = calc() print(c(1), c(2), c(3), c(4), c(5)) :: 8 11 14 17 20
## 람다 표현식 자체를 반환한것으로 람다 표현식을 이용하면 좀더 간단하게 코드를 작성할 수 있다.
## 보통 클로저는 람다 표현식과 함께 사용하는 경우가 많아 둘을 혼동하기 쉬운데
## 람다는 이름이 없는 익명 함수를 뜻하고, 클로저는 함수를 둘러싼 환경을 유지했다가 나중에 다시 사용하는 함수를 뜻한다.
## 클로저의 지역 변수 변경하기
def calc(): a = 3 b = 5 total = 0 def mul_add(x): nonlocal total total = total + a * x + b print(total) return mul_add #함수를 바로 호출하지 않고 리턴으로 함수 자체를 반환함 () 사용하면 안됨. c = calc() c(1) # 8 c(2) # 19 c(5) # 39
## 이전 식에서는 클로저의 지역변수를 가져오기만 하였다면, nonlocal을 통해 클로저의 지역 변수를 변경할 수 있다.
## 클로저(여기서는 calc)의 지역 변수 total = 0을 할당하였다.
## nonlocal total을 통해 바깥쪽 함수의 지역 변수 total을 사용하겠다는 의미로
## total = total + a*x +b로 변경이 가능하다.
## 다음은 a * x + b의 결과를 함수 calc의 지역 변수 total에 누적한다.
## total = total + a*x + b 에서 오른쪽의 total을 삭제하면 값이 누적되지 않는다.
# 클로저는 다소 어려운 개념이므로 지금 당장 완벽하게 이해하지 않아도, 나중에 파이썬에 익숙해지면 자연스럽게 익히게 된다고 한다.
728x90'TIL' 카테고리의 다른 글
TIL. 31 클래스의 속성, 비공개 속성 사용하기 (0) 2020.11.08 TIL.30 클래스와 메서드 사용하기 (0) 2020.11.07 TIL. 28 람다 표현식(lambda) 사용하기 (0) 2020.11.05 TIL. 27 재귀 호출(recursive call) (0) 2020.11.04 TIL.26 함수 매개변수 및 인수 (0) 2020.11.03