Python에서 함수(Function)는
특정 작업을 하나로 묶어두고, 필요할 때마다 꺼내 쓸 수 있도록 만든 코드 블록이다.
우리는 이미 print(), len(), input() 같은 다양한 내장 함수를 자연스럽게 사용하고 있다.
하지만 실제로 프로그램을 만들다 보면,
내가 원하는 동작을 직접 정의해야 하는 순간이 자주 등장한다.
이때 사용하는 것이 바로 사용자 정의 함수(User Defined Function) 이다.
함수를 사용하는 이유는 생각보다 단순하다.
- 같은 코드를 여러번 작성하지 않기 위해
- 기능별로 코드를 분리하여 구조를 명확하게 하기 위해
- 유지보수를 쉽게 하기 위해
- 코드의 재사용성을 높이기 위해
개발을 하다 보면 이런 순간이 있다.
“이 계산, 앞으로도 여러 번 사용할 것 같은데?”
바로 그때 함수를 만들면 된다.
한 번 잘 만들어둔 함수는 필요할 때마다 호출해서 사용할 수 있고, 코드의 가독성도 자연스럽게 좋아진다.
특히 프로젝트 규모가 커질수록 함수의 중요성은 더욱 커진다.
작은 프로그램에서는 단순한 편의 기능처럼 느껴질 수 있지만,
큰 프로젝트에서는 코드를 체계적으로 관리하기 위한 핵심 요소가 되기 때문이다.
1. 사용자 정의 함수
사용자 정의 함수란, 말 그대로 사용자가 직접 만든 함수로
사용자가 특정 작업을 수행하기 위해 직접 작성한 함수를 말한다.
def 함수명(매개변수1, 매개변수2):
실행할 코드
return 반환값

처음엔 단순히 '함수를 만든다'로 생각했었는데
원리를 살펴보면 Python 내부에선 함수가 하나의 객체로 생성되어 메모리에 저장된다.
즉, 함수도 단순한 문법이 아닌 실제 메모리(Heap) 위에 생성되는 객체라고 보면된다.
※ 힙(Heap): 프로그램 실행 중 생성되는 객체들이 저장되는 메모리 공간
2. 함수는 어떻게 만들어질까?
'코드가 저장된다' 정도로 생각하지만,
def hello():
print("안녕하세요")
hello() # 함수호출
- 함수 객체 생성
- hello 라는 이름이 해당 함수 객체를 참조
- 이후 hello() 로 함수 호출 가능
즉, 함수 이름은 함수 자체가 아니라
함수 객체를 가리키는 이름(reference)에 가깝다.
※ 변수도 객체를 참조하는 방식으로 동작하는데, 함수도 동일한 구조로 관리된다.
함수 이름은 변수처럼 사용할 수 있다.

즉, hello, a 둘 다 동일한 함수 객체를 바라보고 있는 상태이다.
3. 함수 객체(Function Object)
function object
├── 함수 이름
├── 매개변수 정보
├── 실행 코드 정보
├── 기본값 정보
├── 전역 네임스페이스 정보
└── 클로저 정보

- hello → 함수 객체 자체
- hello.__code__ → 실제 실행될 코드 정보
즉, 함수는 단순히 실행 가능한 코드 덩어리가 아니라
실행에 필요한 다양한 정보를 담고 있는 객체라고 보면 된다.
그리고 이 함수 객체 자체는 힙(Heap) 메모리 영역에 저장된다.
4. 함수 호출 시 메모리에서 일어나는 일
def add(a, b):
result = a + b
return result
x = add(3, 5)
- add 함수 객체 찾기
- 함수 실행을 위한 새로운 공간 생성
- 매개변수 a, b 에 값 연결
- 함수 코드 실행
- return 값 반환
- 함수를 실행했던 공간 제거
여기서 중요한 것은
함수가 호출될 때마다 새로운 실행 공간이 생성된다는 점이다.
함수 호출 프레임
함수가 호출될 때 생성되는 실행 공간을
함수 호출 프레임(Call Frame) 또는 스택 프레임(Stack Frame) 이라고 한다.
이 공간은 스택(Stack) 메모리 영역에서 관리되며
해당 공간에는 '매개변수, 지역변수, 반환위치, 실행상태'의 정보들이 저장된다.
그리고 함수가 종료되면 해당 정보들은 스택에서 제거된다.
※ 스택(Stack): 함수 호출 순서와 실행 상태를 관리하는 메모리 공간
함수 호출 프레임의 특징
- 함수마다 독립적인 실행 공간이 존재
- 지역 변수 충돌이 발생하지 않음
- 함수 호출이 끝나면 지역 변수는 사라짐
흔히 말하는 "지역 변수는 함수 밖에서 사용할 수 없어"
라는 이유도 결국 함수 호출 프레임 구조와 연결된 정의이다.
5. 함수 만들기
5_1) 매개변수와 반환값이 없는 함수
def func1():
print("처음으로 만드는 함수!")
func1()
for i in range(3):
func1()
# 출력결과 :
# 처음으로 만드는 함수!
# 처음으로 만드는 함수!
# 처음으로 만드는 함수!
# 처음으로 만드는 함수!
5_2) 매개변수가 있는 함수
매개변수가 1개인 경우
def func2(num):
print(f"입력받은 숫자: {num}")
func2(10)
func2(4)
# 출력결과 :
# 입력받은 숫자: 10
# 입력받은 숫자: 4
매개변수가 여러개인 경우
def func3(start, end):
total = 0
for i in range(start, end + 1):
total += i
print(f"{start}부터 {end}까지의 합: {total}")
func3(1, 10)
func3(1, 100)
# 출력결과 :
# 1부터 10까지의 합: 55
# 1부터 100까지의 합: 5050
여기서 사용된 start, end, i, total 은
모두 함수 내부에서만 존재하는 지역 변수(Local Variable)이다.
이 변수들은 함수 호출 시 생성된 스택 프레임 안에 저장되며,
함수가 모두 실행되고 나면 함께 제거된다.
- 함수 호출 → 스택 공간 생성
- 함수 종료 → 스택 공간 제거
6. return과 반환값
함수에서 값을 돌려줄때는 return 키워드를 사용한다.
하지만 return 은 단순히 값을 반환하는 기능만 하는것은 아니다.
retuen이 실제 하는 일
- 함수 실행 종료
- 반환값 전달
- 함수 실행 공간 정리
6_1) 반환값이 없는 함수
return 을 작성하지 않을 경우
파이썬은 자동으로 None 을 반환하도록 되어있다.

6_2) 반환값이 있는 함수

6_3) 계산 결과 반환하기

함수가 return을 만나게 되면 즉시 실행이 종료되고,
결과값이 호출한 위치로 전달된다.
그리고 해당 함수의 스택 프레임 역시 함께 제거된다.
7. 함수 호출은 스택구조로 동작한다.
함수 호출은 기본적으로 스택(Stack)구조로 관리되고있다.
스택구조는 LIFO(Last In First Out) 구조로
가장 나중에 호출된 함수가 가장 먼저 종료되는 구조이다.

예를 들어 위 코드처럼 함수 안에서 또 다른 함수를 호출할 경우
- 새로운 함수가 스택 위에 추가됨 (함수를 호출하면, 실행중이던 함수 위에 새로운 함수가 쌓임)
- 실행이 끝난 뒤 제거 (새로 호출된 함수의 작업이 완료되면, 스택에서 완전히 지워짐)
- 다시 이전 함수로 돌아감 (방금 끝난 함수 바로 밑에 있던 이전 함수로 돌아가, 멈췄던 부분부터 다시 실행을 이어감)
8. 기본값
매개변수에 넣을 값이 없을때를 대비하여 기본값을 설정할 수 있다.
def func6(num1=0, num2=0):
return num1 + num2
print(func6())
print(func6(10))
print(func6(10, 3))
print(func6(num2=3))
💡기본 매개변수 사용 시 주의할 점
아래 코드는 위험한 코드이다.

리스트가 함수 호출을 진행할 때 마다 새롭게 리스트가 생성되는 것이 아닌,
함수 생성 시 한 번 만들어지고 해당 리스트를 유지하고 있기 때문이다.
안전하게 사용하는 방법

9. None이란?
None은 '값이 존재하지 않음'을 의미하는 특별한 객체이다.
None의 특징은 아래와 같다.
- return 이 없으면 자동 반환
- 변수 초기화에 자주 사용
- False 로 판별됨
- 0, [], "" 와는 의미가 다름
None 비교는 is 사용
x = None
print(x is None)
None 은 단순한 값이 아니라 객체이기 때문에
== (비교연산자) 사용이 아닌 is를 사용하여 비교해야 한다.
10. 가변 매개변수 (*args)
매개변수 개수가 정해지지 않았을 경우 가변 매개변수를 사용할 수 있다.
가변 매개변수는 함수를 만들 때 몇개의 값이 들어올지 모르는 상태에서
여러개의 값을 한 번에 받을 수 있도록 해주는 기능이 있다.
매개변수 이름 앞에 별 기호(* 또는 **)를 붙여서 사용할 수 있으며
전달받은 값들은 튜플 형태로 저장되는 특징이 있다.

* 을 이용한 언패킹

*numbers 는 리스트나 튜플 안의 값을 개별 요소로 풀어서 전달하는 역할도 한다.
11. 키워드 매개변수
매개변수 이름을 직접 지정하여 전달하는 방식이다.
키워드 매개변수는 순서가 달라도
매개변수 이름을 기준으로 값이 전달된다.
def func9(id, name, age):
print(f"아이디: {id}")
print(f"이름: {name}")
print(f"나이: {age}")
func9(age=20, id="apple", name="김사과")
딕셔너리와 함께 사용하기
** 는 딕셔너리를 키워드 인자로 풀어서 전달한다.
즉, 딕셔너리의 key-value 구조를 함수 매개변수에 연결해주는 방식이다.
dic1 = {
"age": 25,
"id": "banana",
"name": "반하나"
}
func9(**dic1)
12. 여러개의 반환값
파이썬 함수는 여러 값을 동시에 반환할 수 있다.
def func10(num1=0, num2=0):
# 콤마를 기준으로 여러개 리턴가능
return num1+num2, num1-num2, num1*num2, num1/num2
# 리턴값 4개를 패킹하여 튜플로 나타냄
result = func10(10, 3)
print(result)
# 출력결과 : (13, 7, 30, 3.3333333333333335)
실제로는 여러 값을 반환하는 것이 아니라,
튜플로 패킹되어 반환되고 있는 것이다.
그리고 이후 언패킹하여 사용할 수도 있다.
def func10(num1=0, num2=0):
return num1+num2, num1-num2, num1*num2, num1/num2
result = func10(10, 3)
# 언패킹하여 출력하는 방법
result1, result2, result3, result4 = func10(10, 3)
print(f'덧셈:{result1}')
print(f'뺄셈:{result2}')
print(f'곱셈:{result3}')
print(f'나눗셈:{result4}')
# 출력결과 :
# 덧셈:13
# 뺄셈:7
# 곱셈:30
# 나눗셈:3.3333333333333335
_(언더스코어) 사용하기
필요 없는 값은 _(언더스코어)로 무시할 수도 있다.
def func10(num1=0, num2=0):
return num1+num2, num1-num2, num1*num2, num1/num2
result = func10(10, 3)
# 언더스코어 사용
_, _, result3, _ = func10(10, 3)
print(f'곱셈: {result3}')
print(f'언더스코어: {_}') # result의 마지막값(나눗셈)을 반환
# 출력결과 :
# 곱셈: 30
# 언더스코어: 3.3333333333333335
해당 방식은 반복문에서도 사용할 수 있다.
li1 = [10, 20, 30, 40, 50]
for _, v in enumerate(li1): # 인덱스값을 받지 않을수도 있음.
print(f'값: {v}')
# 출력결과 :
# 값: 10
# 값: 20
# 값: 30
# 값: 40
# 값: 50
마무리
이번 글에서는 단순히 함수를 만드는 방법만이 아니라,
함수가 메모리에 어떻게 저장되는지,
함수 호출 시 어떤 공간이 생성되는지,
return 이 어떤 방식으로 값을 돌려주는지까지 함께 정리해보았다.
특히 Python에서는 함수도 하나의 객체로 동작한다는 점과,
함수 호출이 스택 구조로 관리된다는 부분이 가장 중요하게 느껴졌다.
'Python > 기초' 카테고리의 다른 글
| 파이썬 객체지향 프로그래밍 핵심 개념 정리(클래스와 메서드) (0) | 2026.05.28 |
|---|---|
| 파이썬 함수 심화 (변수 범위, 클로저, 콜백, 람다) (0) | 2026.05.28 |
| 파이썬 반복문 심화 정리(enumerate, zip, iterable, iterator, comprehension) (0) | 2026.05.27 |
| 파이썬 반복문의 기초 정리(While문, for문, range 함수, continue, break, pass) (0) | 2026.05.27 |
| 파이썬 조건문(if, elif, else, 조건부 표현식, 구조적 패턴 매칭 math case) (0) | 2026.05.26 |