PYTHON

파이썬 함수 def — 여섯 줄 복붙을 하나로, return 없으면 None

PANCO_IT
#파이썬#python#함수#def#return#None#매개변수#인자#기본값#파이썬기초#파이썬독학

밀가루를 뿌린 나무 판 위에 별과 나무 모양 쿠키 커터가 놓여 있고, 반죽에는 같은 모양이 여러 번 찍혀 있다

이미지 출처: Pexels · Pexels License

카페 주문서를 출력하는 코드를 넘겨받았습니다. 손님 한 명에 6줄, 3명이면 18줄. 그 6줄은 이렇게 생겼습니다.

price = 4500
count = 2
total = price * count
vat = int(total * 0.1)
print("아메리카노", count, "잔")
print("합계", total + vat, "원")
아메리카노 2 잔
합계 9900 원

같은 여섯 줄이 손님 수만큼 아래로 복붙돼 있습니다. 그리고 의뢰가 하나 붙어 왔습니다. 부가세율이 바뀌면 한 곳만 고치게 해 달라. 지금은 0.1이 세 곳에 흩어져 있어서, 하나라도 빠뜨리면 손님마다 다른 세율이 붙습니다.

의뢰서 한 문장에 조건이 몇 개일까요

의뢰서의 문장은 하나. 그런데 안에는 조건이 셋입니다.

  • 같은 계산·출력을 한 곳에만 적어 둔다
  • 손님마다 다른 값(이름·가격·잔 수)은 밖에서 넣는다
  • 세율은 평소 값을 정해 두되 필요하면 바꿀 수 있게 한다

셋을 한꺼번에 만족시키는 도구가 파이썬에 있습니다. 이름은 함수.

함수가 뭔가요 — 이름표를 붙인 코드 묶음

파이썬 공식 자습서(2026년 9월 10일 기준 3.14판) 4.8절은 이렇게 적습니다. 키워드 def는 함수 정의를 시작합니다. 함수 이름과 괄호로 싸인 형식 매개변수 목록이 뒤따르고, 함수의 바디를 이루는 문장들은 다음 줄에서 들여쓰기로 시작합니다.

쓰는 자리는 정해져 있습니다. 같은 일을 두 번 이상 할 때, 그리고 그 일에 이름을 붙이면 읽기 쉬워질 때. 쿠키 커터를 떠올리면 가깝습니다. 틀은 하나인데 반죽을 갈아 넣으면 같은 모양이 몇 개든 나옵니다. 틀을 고치면 그다음부터 찍히는 모양이 전부 바뀝니다.

성질은 넷입니다.

성질내용
정의와 실행이 따로def 줄은 만들어 두기만 합니다. 이름 뒤에 괄호를 붙여 불러야 실행됩니다
값을 받는다괄호 안 이름으로 밖에서 값을 받습니다
값을 돌려준다return으로 결과를 호출한 자리로 보냅니다
안과 밖이 나뉜다함수 안에서 만든 이름은 밖에서 보이지 않습니다

이 편에서 쓸 말도 여기서 정하고 갑니다.

용어
정의def로 함수를 만들어 두는 일
호출이름 뒤에 괄호를 붙여 실제로 실행하는 일
매개변수정의할 때 괄호 안에 적는 이름. 받을 자리
인자호출할 때 괄호 안에 넣는 값. 그 자리에 들어가는 것
반환값return이 호출한 자리로 보내는 값

def 한 줄에 부품이 넷

가장 짧은 함수부터 만들어 보겠습니다. 이름과 잔 수를 받아 한 줄 찍는 함수입니다.

가장 짧은 함수 — 정의하고 부르기
def order(name, count):
  print(name, count, "잔")

order("아메리카노", 2)
order("라떼", 1)

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

아메리카노 2 잔
라떼 1 잔

첫 줄에 부품이 넷 들어 있습니다. def, 이름, 괄호 안 매개변수, 그리고 콜론. 콜론 다음 줄부터 들여쓴 부분이 함수의 몸입니다. 들여쓰기가 끝나는 곳이 함수가 끝나는 곳입니다.

def order(name, count): 한 줄을 확대해 네 부품을 가리킨 그림. def는 정의 시작 키워드, order는 함수 이름, 괄호 안 name과 count는 매개변수, 콜론 다음 줄부터 들여쓴 두 줄이 함수의 몸이며 들여쓰기가 끝나면 함수도 끝난다는 주석이 붙어 있다

def 줄에 부품이 넷입니다. 들여쓰기가 함수의 경계라는 점은 6편의 if와 같습니다.

order("아메리카노", 2)에서 괄호 안에 넣은 "아메리카노"2가 인자입니다. 이 값들이 정의 쪽 namecount 자리에 순서대로 들어갑니다. 순서가 곧 짝입니다. 자리를 바꿔 order(2, "아메리카노")라고 쓰면 에러 없이 2 아메리카노 잔이 찍힙니다. 파이썬은 순서만 맞춰 넣을 뿐, 그게 말이 되는지는 보지 않습니다.

열여덟 줄을 아홉 줄로

이제 의뢰를 처리합니다. 복붙된 여섯 줄을 통째로 함수 안으로 옮기고, 손님마다 달랐던 값만 매개변수로 빼냅니다.

의뢰 처리 — 세율은 한 곳에만
def receipt(name, price, count, rate=0.1):
  total = price * count
  vat = int(total * rate)
  print(name, count, "잔")
  print("합계", total + vat, "원")

receipt("아메리카노", 4500, 2)
receipt("라떼", 5500, 1)
receipt("녹차", 3800, 3)

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

아메리카노 2 잔
합계 9900 원
라떼 1 잔
합계 6050 원
녹차 3 잔
합계 12540 원

rate=0.1이 의뢰의 핵심입니다. 매개변수에 등호로 값을 붙여 두면 기본값이 됩니다. 호출할 때 안 넣으면 이 값이 쓰이고, 넣으면 넣은 값이 이깁니다. 세율이 12%로 바뀌면 고칠 곳은 def 줄 하나입니다. 손님이 백 명이어도 마찬가지입니다.

특정 손님만 다른 세율을 적용해야 하면 그 자리만 값을 넘깁니다.

receipt("케이크", 6000, 1, 0.05)
케이크 1 잔
합계 6300 원

복붙 코드와 함수로 묶은 코드를 같은 화면에서 비교한 그림. 왼쪽은 손님 세 명에게 여섯 줄씩 열여덟 줄이 반복되고 0.1이 세 곳에 빨간색으로 흩어져 있으며, 오른쪽은 def receipt 여섯 줄과 호출 세 줄로 아홉 줄이고 rate 기본값 0.1이 한 곳에만 초록색으로 있다

같은 결과를 내는 두 코드입니다. 고칠 자리가 셋에서 하나로 줄었다는 점이 줄 수보다 중요합니다.

계산한 값은 어디로 가나요

receipt는 화면에 찍기만 할 뿐 결과를 남기지 않습니다. 세 손님의 합계를 더해 하루 매출을 내려면 값이 손에 남아야 합니다. 그 일을 return이 합니다.

return이 값을 호출한 자리로 보냅니다
def total_price(price, count, rate=0.1):
  total = price * count
  return total + int(total * rate)

a = total_price(4500, 2)
b = total_price(5500, 1)
print(a, b, "합계", a + b)

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

9900 6050 합계 15950

return total + int(total * rate)가 실행되는 순간 함수는 거기서 끝납니다. 아래에 줄이 더 있어도 실행되지 않습니다. 그리고 계산된 값이 total_price(4500, 2)라고 적은 그 자리로 돌아가, 마치 처음부터 9900이라고 쓴 것처럼 대입됩니다.

호출부터 반환까지의 시간 순서를 가로 타임라인으로 그린 그림. a = total_price(4500, 2) 자리에서 출발해 함수 안으로 들어가 total = 9000 계산, return 9900 실행, 다시 호출한 자리로 값 9900이 돌아와 a에 담기는 네 시점이 표시되어 있고, return 아래 줄은 실행되지 않는다는 표시가 붙어 있다

호출한 자리를 떠났다가 값을 들고 그 자리로 돌아옵니다. return을 만나면 함수는 즉시 끝납니다.

검수 — 아무것도 안 나올 때

의뢰를 넘기기 전에 걸리기 쉬운 자리를 짚어 봅니다. 제가 이 편에서 가장 공들여 말하는 대목입니다.

계산은 맞는데 None이 찍히는 일이 있습니다.

return을 빠뜨리면
def total_price(price, count):
  price * count

print(total_price(4500, 2))

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

None

공식 자습서 4.8절이 이 상황을 그대로 설명합니다. return 문이 없는 함수도 값을 돌려주는데, 그 값이 None입니다. 계산은 제대로 했지만 결과를 밖으로 보내지 않았으니 호출한 자리에는 아무것도 남지 않은 셈입니다. price * countreturn price * count로 고치면 9000이 나옵니다.

한 가지 더. 값을 안 돌려주는 함수를 그냥 부르기만 하면 화면에 None이 보이지 않습니다. 자습서 표현대로 인터프리터가 None 출력을 억제하기 때문입니다. print()로 감싸는 순간 정체가 드러납니다. 그래서 이 함정은 조용히 지나가다 나중에 계산이 틀어질 때 발견되는 편입니다.

괄호를 빠뜨리는 실수도 흔합니다.

print(total_price)
<function total_price at 0x7f3c...>

에러가 아닙니다. 괄호가 없으면 실행하지 않고 함수 그 자체를 가리킵니다. 값이 필요한 자리에 함수를 넣어 둔 셈이라, 다음 계산에서 엉뚱한 에러가 납니다.

정의보다 먼저 부르는 경우는 에러로 잡힙니다.

greet("손님")

def greet(name):
    print(name, "안녕하세요")
NameError: name 'greet' is not defined

파이썬은 위에서 아래로 읽습니다. def 줄을 아직 지나지 않았으면 그 이름은 없는 이름입니다. 함수 정의는 부르는 곳보다 위에 둡니다.

손님이 열 명이면 몇 줄이 될까요

의뢰를 끝내고 두 코드의 길이를 세어 봤습니다.

복붙 코드와 함수 코드의 줄 수를 나란히 놓은 가로 막대 그림. 손님 세 명일 때 복붙은 18줄 함수는 9줄, 다섯 명일 때 복붙은 30줄 함수는 11줄, 열 명일 때 복붙은 60줄 함수는 16줄이며, 세율을 고칠 자리는 각각 손님 수만큼과 언제나 한 곳이라는 표시가 붙어 있다

손님이 늘수록 벌어집니다. 함수 쪽은 호출 한 줄씩만 늘어나고, 고칠 자리는 손님이 몇이든 하나입니다.

줄 수보다 오래 남는 이득은 고칠 자리가 하나라는 점입니다. 복붙 코드에서 세율을 바꾸려면 세 곳을 빠짐없이 찾아야 하고, 손님이 열 명이면 열 곳입니다. 하나를 놓치면 그 손님만 조용히 다른 값이 찍힙니다. 에러가 안 나는 실수라 더 늦게 발견됩니다.

함수 하나로 본 문법 일곱 개

문법이 글의 예
def 이름(매개변수):함수를 정의한다def receipt(name, price, count):
들여쓰기함수의 몸. 끝나면 함수도 끝def 아래 네 줄
호출이름 뒤 괄호로 실행receipt("라떼", 5500, 1)
인자호출할 때 넣는 값. 순서대로 짝"라떼", 5500, 1
return 값값을 호출한 자리로 보내고 즉시 끝return total + vat
기본값안 넣으면 쓰이는 평소 값rate=0.1
Nonereturn이 없을 때 돌아오는 값print(total_price(...))

def, 괄호, return. 셋만 자리를 알면 나머지는 따라옵니다. 그리고 오늘 사건의 재발 방지 수칙은 한 줄입니다. 값이 필요하면 return을 쓴다.

한 줄만 바꾸면 출력은 어떻게 될까요

미니 퀴즈

OX 3문제 + 4지선다 4문제

참고 자료


포켓코딩(PYTHON1) 10편 · 9편(파이썬 while 반복문)에서 이어집니다. 예제 코드는 강사 개인 GitHub 저장소의 강의 자료를 바탕으로 다시 썼습니다. 다음 편은 딕셔너리입니다. 순서가 아니라 이름표로 값을 꺼내는 {}와, 없는 열쇠를 물었을 때 나는 KeyError를 다룹니다.

PANCO_IT

IT 지식을 정리하는 개발자

관련 글