오늘도 개발
CPython이 함수를 처리하는 방법 본문
CPython이란?
파이썬은 인터프리터에 따라 다르게 구현된다.
함수를 다루는 방법도 인터프리터에 따라 다르다.
CPython은 가장 널리 사용되는 파이썬 인터프리터이다.
여기서는 Cpython이 어떻게 함수를 처리하는지 알아보자.
1. 함수 정의
변수가 값에 대한 이름표라면 함수는 코드에 대한 이름표이다.
파이썬에서는 함수도 오브젝트이며 변수와 같은 방식으로 메모리에 저장된다.
우리가 함수를 정의하면 Cpython은 힙 메모리에 함수 오브젝트를 생성하고
스택 메모리에 함수 이름을 쓰고 함수 오브젝트의 주소를 저장한다.
즉, 함수 이름은 함수 오브젝트를 바인딩한다.
# 정의
def sayHi():
print("hello, world")
# 힙 메모리에 있는 sayHi 함수 오브젝트의 주소
print(id(sayHi))
# 4455101488
# 호출
sayHi()

그렇다면 함수를 호출하면 무슨 일이 일어날까?
2. 함수 호출과 프레임(Frame)
Cpython은 코드를 실행할 때 스택 메모리에 상자를 만들고
코드를 실행하기 위해 필요한 것을 담는다.
이 상자를 프레임이라고 한다.
프로그램을 실행하면 Cpython은 제일 먼저 전역 프레임이라는 상자를 만든다.
그리고 함수 바깥 영역(전역)에 있는 모든 변수와 함수 이름을 저장한다.
프로그램을 실행하다가 함수를 호출해야하면 전역 프레임 위에 함수의 프레임을 만든다.
이 상자에는 함수 안에 있는 모든 변수와 함수 이름을 저장한다.
함수 프레임은 함수 실행이 끝나면 자동으로 삭제된다.
def sayHi():
print('Hi')
def sum(x):
y = 2
sayHi()
return x + y
a = 1
b = sum(a)

3. 함수가 변수를 찾는 방법 - LEGB 규칙
파이썬은 변수의 값을 찾아볼 때 LEGB 규칙을 따른다.
L : Local, 함수 안
E : Enclosed function locals, 외부함수가 있는 경우 외부함수
G : Global, 함수 바깥
B : Built-in, 파이썬 내장 함수(open, range 등)
함수 안에서 먼저 찾아보고, 없으면 외부함수, 함수 바깥, 내장 함수 순으로 찾아본다는 뜻이다.
아래와 같은 코드에서 1이 아니라 2가 나오는 이유는 x의 값을 함수 안에서 먼저 찾아보기 때문이다.
x = 1
def test():
x = 2
print(x)
test()
# 2
4. 클로저
바깥 함수가 반환한 뒤에도 안쪽 함수가 바깥 함수의 변수를 사용할 수 있는 것을 클로저라고 한다.
아래 예시에서 outside 함수가 클로저이다.
def outside():
num = 1
def inside():
print(num)
return inside
test = outside()
test()
# 1

마지막줄에서 test()를 호출할 때 이미 바깥 함수의 프레임은 삭제된 상태이다.
프레임이 삭제되면서 변수 num도 같이 삭제되는데
test()를 실행하면 변수 num의 값을 오류 없이 출력할 수 있다.
어떻게 이런 일이 가능할까?
파이썬은 함수 오브젝트를 생성할 때 내부 함수인 경우 __closure__라는 속성을 추가한다.
__closure__ 속성에는 내부 함수가 참조하는 바깥 함수의 변수를 튜플 형태로 저장한다.
즉, 내부 함수 오브젝트에는 LEGB에서 E(Enclosed function locals) 범위에 해당하는 변수가 들어있다.
그래서 바깥 함수의 프레임이 없어져도 안쪽 함수는 바깥 함수의 변수를 쓸 수 있는 것이다.
위의 그림을 더 정확히 바꾸면 아래와 같다.

참고
'웹 프로그래밍 > Python3' 카테고리의 다른 글
| 파이썬 객체(Object) (0) | 2022.06.09 |
|---|---|
| 파이썬 함수(function) (0) | 2022.06.07 |
| for문과 range()함수 (0) | 2022.06.05 |
| 파이썬 세트(Set) (0) | 2022.06.05 |
| 파이썬 딕셔너리(Dictionary) (0) | 2022.06.05 |