파이썬에서 코드를 효율적으로 재사용하려면 함수의 개념을 정확히 이해해야 해요. def 키워드로 시작하는 기본 구조부터, 가변 인자와 람다(lambda)를 활용한 유연한 코드 작성법까지 폭넓게 다뤄볼게요. 여기에 더해 나중에 코드를 수정하기 편하도록 돕는 Docstring과 타입 힌트 작성법까지 함께 알아두면 훨씬 더 탄탄한 프로그램을 만들 수 있어요.
목차
- 함수가 없으면 어떻게 될까요? (함수의 필요성)
- 파이썬 함수의 기본 구조와 다중 반환(Multiple Return)
- 나와 팀원을 위한 배려, Docstring (함수 문서화)
- 기본값과 키워드 인자 — 더 편리한 함수 호출
- 몇 개가 올지 모를 땐? *args와 **kwargs
- 한 줄로 끝내는 마법, 람다(lambda) 함수
- 스코프(Scope) — 전역 변수와 지역 변수
- 안전한 코드를 위한 타입 힌트(Type Hint) 기초
- 초보자가 자주 하는 함수 관련 실수 베스트 3
- 자주 묻는 질문
함수가 없으면 어떻게 될까요? (함수의 필요성)
코딩을 하다 보면 똑같은 코드를 여기서도 쓰고, 저기서도 쓰고, 또 다른 파일에서도 복사해서 쓴 경험 한 번쯤 있으시죠? 처음 복사해서 붙여넣기 할 때는 편하지만, 나중에 수정할 일이 생기면 그때부터 진짜 고생이 시작돼요.
만약 텍스트 위아래로 그어주는 구분선의 기호를 하나만 바꾸고 싶다고 가정해 볼게요. 코드를 10군데에 복사해 뒀다면, 그 10곳을 전부 찾아서 일일이 고쳐야 해요. 하나라도 빠뜨리면 화면이 이상하게 나오겠죠. 함수는 바로 이런 지루하고 반복적인 작업을 없애려고 만들어요. 코드를 한 곳에 예쁘게 모아두고 이름표를 붙여둔 다음, 필요할 때마다 그 이름만 불러서 쓰는 거예요.
함수를 쓰기 전과 후의 코드가 어떻게 달라지는지 눈으로 직접 비교해 볼게요.
이제 테두리 모양을 등호(=)에서 하이픈(-) 기호로 바꾸고 싶다면 어떻게 하면 될까요? 복사 붙여넣기를 한 코드는 여러 줄을 수정해야 하지만, 함수를 만들었다면 print_box 안의 테두리 출력 코드 딱 두 줄만 고치면 돼요. 100번을 호출했든 1000번을 호출했든 한 번에 싹 바뀌니까 유지보수가 엄청나게 편해져요.
파이썬 함수의 기본 구조와 다중 반환(Multiple Return)
매개변수와 인자, 그리고 반환값
함수를 만들 때는 def라는 키워드를 써서 정의해요. 여기서 초보자들이 가장 많이 헷갈려하는 용어가 바로 매개변수(parameter)와 인자(argument)예요.
쉽게 비유하자면, 매개변수는 붕어빵을 구울 때 쓰는 ‘빈 틀’ 같은 거고, 인자는 그 틀 안에 실제로 부어 넣는 ‘팥’이나 ‘슈크림’ 같은 실제 재료라고 생각하시면 돼요. 그리고 함수가 안에서 열심히 계산을 끝마치면 return 키워드를 통해 그 결과물(잘 구워진 붕어빵)을 바깥으로 돌려줘요.
def 키워드 뒤에 함수의 이름과 괄호를 적고, 그 안에 받을 재료의 이름을 정해줘요.
콜론(:)을 찍고 다음 줄로 넘어가서 스페이스바 4칸을 띄운 뒤, 실제로 실행할 코드를 쭉 작성해요.
계산이 끝났다면 return 키워드 옆에 돌려줄 값을 적어요. 이 단계를 빼먹으면 파이썬은 아무것도 없다는 뜻의 None을 돌려줘요.
기본적인 함수 구조와 함께 여러 개의 값을 한 번에 돌려받는 코드를 살펴볼게요. 파이썬만의 아주 강력한 기능이랍니다.
복잡한 계산 결과를 바깥으로 꺼내기 위해 굳이 파이썬 전역변수 지역변수 개념을 억지로 섞어 쓰지 않아도 돼요. C나 Java 같은 다른 언어에서는 값을 여러 개 반환하려면 복잡한 구조체나 배열을 새로 만들어야 하지만, 파이썬에서는 쉼표 하나로 깔끔하게 다중 반환이 가능해요. 코드가 훨씬 간결해지겠죠?
나와 팀원을 위한 배려, Docstring (함수 문서화)
내가 방금 열심히 짠 함수도 한 달 뒤에 다시 열어보면 “내가 이걸 왜 이렇게 만들었지?”, “여기엔 무슨 값을 넣어야 하더라?” 하고 까먹기 십상이에요. 그래서 함수를 만들 때는 그 함수 바로 아래에 큰따옴표 세 개(""")를 열고 닫아서 이 함수의 사용법을 꼼꼼하게 적어두는 것이 좋아요. 이를 Docstring이라고 불러요.
파이썬 def 문으로 함수를 정의할 때, 마치 파이썬 공식 문서처럼 예쁘게 설명을 남기는 방법을 확인해 볼게요.
Docstring을 꼼꼼하게 적어두면 나중에 코드를 유지보수할 때 파일을 다 뒤져보지 않아도 이 함수가 무슨 역할을 하는지 바로 알 수 있어요. 협업을 할 때 동료들에게 칭찬받는 아주 좋은 습관이랍니다.
💡 Tip
help() 함수를 굳이 출력하지 않아도, VS Code나 PyCharm 같은 코드 에디터에서 함수 이름 위에 마우스를 가만히 올려두면 작성해둔 Docstring이 팝업 창으로 짠 하고 나타나요. 이게 개발할 때 엄청나게 편리해요.
기본값과 키워드 인자 — 더 편리한 함수 호출
함수를 부를 때 항상 똑같은 값을 넣어야 하는 경우가 있어요. 카페에서 커피를 주문할 때 90%의 손님이 아메리카노를 마신다면, 종업원이 “무슨 커피 드릴까요?” 묻기 전에 기본으로 아메리카노를 세팅해두면 편하겠죠?
파이썬 함수도 마찬가지예요. 거듭제곱을 계산하는데 대부분 제곱(2)만 쓴다면, 굳이 매번 숫자 2를 입력하게 만들 필요가 없어요. 이때 매개변수에 기본값을 설정해 두면 호출할 때 그 값을 생략할 수 있어서 타이핑이 확 줄어들어요.
또한 함수를 호출할 때 순서대로 값을 넣는 대신, 매개변수 이름을 직접 적어서 전달하는 방식을 키워드 인자라고 해요. 이걸 쓰면 순서를 뒤죽박죽으로 넣어도 파이썬이 알아서 제자리를 찾아서 값을 꽂아줘요.
⚠️ Warning
기본값이 있는 매개변수는 반드시 기본값이 없는 매개변수보다 뒤쪽에 적어줘야 해요. 예를 들어 def power(exp=2, base): 처럼 적으면 파이썬이 값을 넣을 때 짝을 맞추지 못해서 곧바로 문법 에러(SyntaxError)를 뱉어내요.
몇 개가 올지 모를 땐? *args와 **kwargs
우리가 함수를 만들 때 값을 2개만 받을지, 아니면 10개를 받을지 미리 알 수 없는 상황이 종종 발생해요. 쇼핑몰 장바구니에 담긴 물건들의 가격을 전부 더하는 함수를 만든다고 생각해 보세요. 어떤 손님은 물건을 1개만 살 수도 있고, 어떤 손님은 50개를 살 수도 있잖아요?
이럴 때 매개변수 앞에 별 기호(*)를 하나 붙여주면, 파이썬이 넘쳐나는 인자들을 모조리 모아서 ‘튜플’이라는 하나의 보따리로 묶어줘요. 반대로 이름표가 달린 키워드 인자들을 무제한으로 받고 싶다면 별을 두 개(**) 붙여주면 돼요. 그러면 파이썬이 이를 ‘딕셔너리’ 형태로 깔끔하게 정리해 줘요.
데이터 개수가 상황에 따라 변할 때는 외부에서 리스트를 억지로 만들어서 넘겨줄 수도 있지만, *args나 **kwargs를 쓰면 함수를 호출하는 쪽의 코드가 훨씬 직관적이고 깔끔해져요. 다른 사람이 만든 라이브러리를 열어보면 이 문법이 정말 밥 먹듯이 나오니까 꼭 눈에 익혀두는 게 좋아요.
한 줄로 끝내는 마법, 람다(lambda) 함수
딱 한 번 쓰고 버릴 아주 간단한 계산을 해야 하는데, 굳이 파일 위쪽에 def를 써가며 거창하게 이름을 지어주고 들여쓰기까지 하기 귀찮을 때가 있죠? 마치 식당에서 쓰는 일회용 나무젓가락처럼 가볍게 쓰고 싶을 때 말이에요.
이때 lambda 키워드를 쓰면 단 한 줄짜리 이름 없는 함수(익명 함수)를 뚝딱 만들 수 있어요. 람다는 특히 리스트 안에 있는 데이터들을 특정한 기준에 따라 정렬하고 싶을 때 sorted() 함수의 key 조건으로 넘겨주기 아주 안성맞춤이에요.
📌 Note
람다는 코드를 짧게 만들어준다는 강력한 장점이 있지만, 너무 복잡한 계산이나 여러 개의 if문을 억지로 구겨 넣으려고 하면 가독성이 최악으로 떨어져요. 코드가 길어질 것 같으면 주저하지 말고 일반적인 def 함수로 따로 빼서 쓰는 것이 훨씬 좋습니다.
스코프(Scope) — 전역 변수와 지역 변수
파이썬에는 변수가 살아 숨 쉴 수 있는 유효 범위, 즉 스코프(Scope)라는 규칙이 있어요. 방 안에서만 가지고 노는 장난감이 있고, 거실에 두고 온 가족이 쓰는 TV가 있는 것처럼요.
함수 안에서 만든 변수는 ‘지역 변수’라고 해서, 함수가 일을 마치고 종료되면 메모리에서 흔적도 없이 완전히 사라져요. 반대로 함수 바깥의 가장 바깥쪽 줄에서 만든 변수는 ‘전역 변수’라고 해요. 이 전역 변수는 함수 안에서 자유롭게 읽어볼 수는 있지만, 내 마음대로 수정해서 값을 덮어쓸 수는 없도록 안전장치가 걸려 있어요.
만약 함수 안에서 바깥에 있는 전역 변수의 값을 꼭 바꿔야만 한다면 global이라는 키워드를 써서 파이썬에게 “나 이거 바깥에 있는 그 변수 수정할 거야”라고 명시적으로 허락을 받아야 해요.
❗ 중요
글로벌(global) 키워드를 너무 많이 쓰면 여기저기서 변수 값을 바꿔버리기 때문에, 나중에 코드가 꼬였을 때 도대체 어떤 함수가 범인인지 추적하기가 굉장히 어려워져요. 스파게티 코드가 되기 쉬우니 가급적 매개변수로 값을 전달받고 return으로 결과를 돌려주는 방식을 추천해요.
안전한 코드를 위한 타입 힌트(Type Hint) 기초
파이썬은 원래 데이터 타입에 아주 관대한 언어예요. 네모난 구멍에 동그란 블록을 밀어 넣어도 일단 프로그램 실행 자체는 시켜주죠. 그런데 이게 꼭 좋은 것만은 아니에요. 함수 안에서 문자열을 잘라야 하는데 누군가 숫자를 집어넣으면, 프로그램이 돌다가 중간에 뻥 터져버리는 끔찍한 일을 겪게 돼요.
이런 초보적인 실수를 미리 막으려면 파이썬 매개변수 인자에 예상되는 데이터 타입이 무엇인지 미리 꼬리표를 달아두면 좋아요. 콜론(:)과 화살표(->)를 활용해서 함수에 타입 힌트를 적용하는 방법을 살펴볼게요.
타입 힌트는 프로그램 실행 속도에 영향을 주거나 강제로 에러를 발생시키지는 않아요. 그저 힌트일 뿐이니까요. 하지만 내가 코드를 짤 때 자동완성을 도와주고, 다른 사람이 내 함수를 쓸 때 잘못된 값을 넣는 실수를 개발 단계에서 사전에 차단해 주는 훌륭한 방어막 역할을 톡톡히 해내요.
초보자가 자주 하는 함수 관련 실수 베스트 3
입문자가 파이썬에서 함수를 처음 만들고 써볼 때 가장 많이 겪게 되는 어이없는 문법, 논리 에러 세 가지를 짚어드릴게요. 사실 처음에는 누구나 다 이 함정에 한 번씩 빠진답니다. 이 실수들의 원인만 정확히 알고 피하셔도, 원인을 못 찾아 밤새워 디버깅하는 시간을 확 줄일 수 있을 거예요.
함수가 기껏 계산을 해서 return을 해줬는데, 그걸 받아줄 변수를 안 쓰면 데이터는 그대로 사라져요. add(3, 5) 라고만 쓰면 계산만 하고 끝납니다. 반드시 result = add(3, 5) 처럼 변수에 담아서 써야 해요.
안에서 print()만 하고 return 문을 아예 안 쓴 함수를 msg = say_hello() 처럼 변수에 할당해 봤자, msg 안에는 아무것도 없다는 뜻의 None만 덩그러니 들어가게 돼요. 결과값이 필요하면 출력(print)이 아니라 반환(return)을 해야 해요.
def profile(age=20, name): 처럼 쓰면 파이썬은 인자가 들어왔을 때 이게 age를 덮어쓰려는 건지 name에 넣으려는 건지 판단을 못 해서 바로 SyntaxError를 발생시켜요. 기본값은 무조건 뒤쪽으로 몰아서 적어주세요.
자주 묻는 질문
Q. 함수에 일반 주석(#)을 다는 것과 Docstring을 작성하는 것은 구체적으로 어떤 차이가 있나요?
일반 주석(#)은 함수 내부에서 “이 for문은 이런 원리로 동작해”라고 특정 복잡한 로직을 동료나 미래의 자신에게 설명할 때 써요. 반면 Docstring(""")은 함수 내부의 구조보다는, 이 함수 전체의 목적이 무엇인지, 어떤 매개변수를 넣어야 하고 어떤 결과가 나오는지를 사용자 관점에서 알려주는 공식 안내서 역할을 해요. 코드를 일일이 다 열어보지 않아도 help() 함수나 편집기 화면에서 마우스만 올려도 설명이 팝업으로 뜬다는 게 가장 큰 장점이자 차이점이에요.
Q. 함수에서 계산된 두 개 이상의 결과값을 동시에 바깥으로 빼내려면 어떻게 해야 하나요?
파이썬만의 강력한 기능인 튜플을 적극적으로 쓰면 아주 간단하게 해결돼요. 함수 마지막에 return a, b, c처럼 쉼표를 기준으로 돌려줄 변수들을 나열하면, 파이썬이 알아서 이를 하나의 튜플 묶음으로 포장해서 반환해요. 그러면 함수를 부른 바깥쪽에서는 result1, result2, result3 = my_function() 형태로 쉽고 깔끔하게 값을 나눠서 저장할 수 있어요.
Q. 함수 매개변수에 실수로 문자열 대신 숫자를 넣어서 에러가 나는 걸 미리 방지할 수 있는 방법이 있나요?
파이썬 3.5 버전부터 도입된 타입 힌트(Type Hint)를 사용해 def greet(name: str) -> str: 같은 형식으로 타입을 명시해 줄 수 있어요. C 언어나 Java처럼 타입이 틀렸다고 컴파일 단계에서 아예 실행을 막아버리는 완벽한 강제성은 파이썬에 없지만, VS Code나 PyCharm 같은 최신 에디터에서 코드를 짜는 즉시 빨간 줄로 경고를 시각적으로 해주기 때문에 버그를 사전에 잡고 예방하기가 훨씬 수월해져요.