[4장] 공식 문서로 알아보는 Python 함수의 모든 것

Jzahnny
July 10, 2025

[4장] 공식 문서로 알아보는 Python 함수의 모든 것

Use Original Cover Image
Type
Post
Children
Language
ko
Tags
Python
function
Argument
Symbol Table
Method
Keyword Argument
Arbitrary Argument
Positional Argument
Lambda Expression
Docstring
Authors
Jzahnny
Published
July 10, 2025

개요

본 글은 Python v3.13 공식 Tutorial의 4.8. Defining Functions ~ 4.10. Intermezzo: Coding Style 의 모든 내용을 보다 이해하기 쉽게 정리한 글이다.

1. 함수 정의

def fib(n): # write Fibonacci series less than n """Print a Fibonacci series less than n.""" a, b = 0, 1 while a < n: print(a, end=' ') a, b = b, a+b print()
 
  1. def : 함수 정의를 한다는 키워드이다.
  1. fib : 함수의 이름을 설정한다.
  1. () : 함수의 parameter를 list 형식으로 넣는다.
  1. : : 이 콜론 다음으로 함수의 Body가 이어진다. 함수 작성시 Identation (들여쓰기)가 필수적으로 요구된다.

2. Symbol Table

symbol table이란 어떤 object에 대해 name과 데이터의 주소를 저장하고 있는 dictionary이다.
  1. 함수를 실행하면 그 함수의 새 symbol table을 만든다.
  1. 변수를 참조할 때는 local symbol table그 함수를 감싸는 함수의 symbol tableglobal symbol tablebuilt-in name의 table 순서로 찾는다.
    1. 그래서 global variable이나 감싸는 함수의 variable의 값이 할당되지 않을 수 있다.
  1. 다른 함수가 call하거나 자기 자신이 재귀적으로 call할 때도 그 call마다 local symbol table을 만든다.
  1. 함수도 symbol table에서 object이기 때문에 다른 name이 같은 함수 object를 가리킬 수도 있다.
fib
page icon
<function fib at 10042ed0>
f = fib f(100)
page icon
0 1 1 2 3 5 8 13 21 34 55 89

3. Return

return statement를 통해 함수내 어떤 value를 내보낼 수 있다.

3.1. return이 없는 함수

앞서 보인 fib 함수는 값을 return statement가 없다. 하지만 python에서는 return이 없더라도 항상 return을 하고 그 값은 None이다. None은 interpreter에서 안 보이게 되기 때문에 억지로 None이 return 됨을 보고 싶다면 print를 쓸 수 있다.
print(fib(0))
page icon
None

3.2. Method

def fib2(n): # return Fibonacci series up to n """Return a list containing the Fibonacci series up to n.""" result = [] a, b = 0, 1 while a < n: result.append(a) # see below a, b = b, a+b return result f100 = fib2(100) # call it f100 # write the result
page icon
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
method는 함수이며 이름은 obj.methodname 로 정해진다. 이때 obj는 어떤 object이고 str, int와 같이 expression일 수 있다. 위 예시에서는 result.append(a) 라는 statement가 list object인 result 의 method를 call한 것이다.
 
  • object의 type마다 서로 다른 method를 가지고 있다.
    • 예를 들어 list object에는 append가 있지만 어떤 object에는 append가 없을 수 있다.
  • object type이 다르지만 서로 name이 같아도 ambiguity를 일으키지 않는다.
  • class를 통해 직접 object type과 method를 정의할 수 있다.
    • 예를 들어 temp라는 type의 object를 만들고 method로 append를 추가해도 list의 append와 충돌하지 않는다.
       

4. Argument

4.1. Default Argument Values

함수를 정의할 때 여러개의 argument를 둘 수 있으며 argument마다 기본 값을 설정할 수 있다.
def으로 정의할 때 retries=4 와 같이 기본값을 설정하는 것이다.
def ask_ok(prompt, retries=4, reminder='Please try again!'): while True: reply = input(prompt) if reply in {'y', 'ye', 'yes'}: return True if reply in {'n', 'no', 'nop', 'nope'}: return False retries = retries - 1 if retries < 0: raise ValueError('invalid user response') print(reminder)
기본값을 설정해두면 함수 호출시 argument의 값을 명시하지 않아도 호출할 수 있다. 예를 들어 아래와 같이 3개를 모두 명시하지 말고 1개나 2개만 명시할 수 있다.
  • 필수 argument만 전달: ask_ok('Do you really want to quit?')
  • 선택 argument 1개 전달: ask_ok('OK to overwrite the file?', 2)
  • 모든 argument 전달: ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')

4.1.1. 주의할 점

argument의 기본값은 함수가 정의될 떄 딱 한 번만 정해진다. 예를 들어 아래 코드에서는 함수가 정의될 때 i=5였기 때문에 arg=5로 확정된다. 따라서 i=6을 거쳐도 f를 호출시 5가 출력된다.
i = 5 def f(arg=i): print(arg) i = 6 f()
 
만일 기본값이 list, dictionary, class의 instance와 같이 mutable하다면 함수 호출시 모두 같은 object를 공유하게 된다. 예컨대 아래와 같이 L=[]로 기본값을 정해두면 호출 때마다 같은 list에 값을 추가하므로 오른쪽과 같이 출력된다.
def f(a, L=[]): L.append(a) return L print(f(1)) print(f(2)) print(f(3))
page icon
[1] [1, 2] [1, 2, 3]
 
기본값을 공유하기 싫다면 아래와 같이 수정할 수 있다.
def f(a, L=None): if L is None: L = [] L.append(a) return L

4.2. Keyword Arguments

함수 호출시 kwarg=value 꼴로 argument를 전달할 수 있다.
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'): print("-- This parrot wouldn't", action, end=' ') print("if you put", voltage, "volts through it.") print("-- Lovely plumage, the", type) print("-- It's", state, "!")
예를 들어 위 함수를 아래처럼 호출할 수 있다.
parrot(1000) # 1 positional argument parrot(voltage=1000) # 1 keyword argument parrot(voltage=1000000, action='VOOOOOM') # 2 keyword arguments parrot(action='VOOOOOM', voltage=1000000) # 2 keyword arguments parrot('a million', 'bereft of life', 'jump') # 3 positional arguments parrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword
이때 아래와 같은 특징이 있다.
  • 모든 keyword arguments는 함수의 argument 중 하나와 반드시 맞아야 한다. (아래 예시에서 parrot에 없는 actor argument를 전달하면 안 된다.)
  • keyword argument 끼리는 순서가 상관이 없다.
  • argument가 중복되면 안 된다.
  • non-keyword argument가 keyword argument 다음에 와서는 안된다.
parrot() # required argument missing parrot(voltage=5.0, 'dead') # non-keyword argument after a keyword argument parrot(110, voltage=220) # duplicate value for the same argument parrot(actor='John Cleese') # unknown keyword argument
 

4.3. Arbitrary Argument: *args, **kwargs

4.3.1. Arbitrary Positional Argument: *args

*name 형식으로 argument를 두어 임의의 개수의 argument를 호출할 수 있다. 이때 아래와 같은 특징이 있다.
  • 여러 argument는 tuple로 보내진다.
  • *arg 다음의 parameter는 keyword-only arguments 이다.
def concat(*args, sep="/"): return sep.join(args) concat("earth", "mars", "venus", sep=".")
page icon
'earth.mars.venus'

4.3.2. Arbitrary Keyword Argument: **kwargs

*name은 tuple로 받지만 **name은 dictionary로 받는다. 마찬가지로 임의 개수의 argument를 keyword로 전달할 수 있다. 이때 *name 이후에 **name이 봐야한다. 또한 dictionary 내 argument의 순서는 호출시 표기한 순서와 같다.
def cheeseshop(kind, *arguments, **keywords): print("-- Do you have any", kind, "?") print("-- I'm sorry, we're all out of", kind) for arg in arguments: print(arg) print("-" * 40) for kw in keywords: print(kw, ":", keywords[kw]) cheeseshop("Limburger", "It's very runny, sir.", "It's really very, VERY runny, sir.", shopkeeper="Michael Palin", client="John Cleese", sketch="Cheese Shop Sketch")
-- Do you have any Limburger ? -- I'm sorry, we're all out of Limburger It's very runny, sir. It's really very, VERY runny, sir. ---------------------------------------- shopkeeper : Michael Palin client : John Cleese sketch : Cheese Shop Sketch
 

4.4. Special parameters

가독성과 성능을 위해 선택적으로 /*를 써서 argument를 어떻게 함수에게 전달할지의 방법을 제한시킬 수 있다.
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2): ----------- ---------- ---------- | | | | Positional or keyword | | - Keyword only -- Positional only

4.4.1. Positional-or-Keyword Arguments

함수를 정의할 때 /* 가 없다면 position으로 전달하거나 keyword로 전달할 수 있다. 지금까지 살펴본 것과 같다.

4.4.2. Positional-Only Parameters

/ 이전에 놓인 parameter는 반드시 position으로만 전달되고 keyword로 전달할 수 없다.

4.4.3. Keyword-Only Arguments

* 후에 놓인 parameter는 반드시 keyword로만 전달되고 position으로 전달할 수 없다.

4.4.4. 사용 예시

def standard_arg(arg): print(arg) def pos_only_arg(arg, /): print(arg) def kwd_only_arg(*, arg): print(arg) def combined_example(pos_only, /, standard, *, kwd_only): print(pos_only, standard, kwd_only)
 
/*를 두지 않으면 어떤 제약도 없다.
standard_arg(2)
page icon
2
standard_arg(arg=2)
page icon
2
positional only argument 자리에 keyword로 전달하면 오류가 난다.
pos_only_arg(1)
page icon
1
pos_only_arg(arg=1)
page icon
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: pos_only_arg() got some positional-only arguments passed as keyword arguments: 'arg'
keyword only argument 자리에 position으로 전달하면 오류가 난다.
kwd_only_arg(3)
page icon
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given
kwd_only_arg(arg=3)
page icon
3
 

4.4.5. Positional argument와 **kwrds 의 충돌

아래의 왼쪽 예시처럼 **kwds에서 positional argument에 이미 있는 값을 전달하려고 하면 에러가 난다. 먼저 정의되고 전달된 positional argument가 먼저 저장되기 때문이다.
이 문제를 오른쪽 예시처럼 /을 두어 name을 positional only argument로 두면 오류가 발생하지 않는다.
def foo(name, **kwds): return 'name' in kwds foo(1, **{'name': 2})
page icon
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: foo() got multiple values for argument 'name'
def foo(name, /, **kwds): return 'name' in kwds foo(1, **{'name': 2})
page icon
True
 

4.4.6. 정리

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
  1. positional-only를 쓰면 좋은 상황
    1. parameter name이 어떠한 의미가 없다.
    2. user에게 parameter name를 보이게 하고 싶지 않다.
    3. argument의 순서를 더 명확히 정하고 싶다.
    4. positional parameter와 arbitrary keywords를 둘 다 사용하려 한다.
    5. API를 위한 함수이다. 나중에 parameter name이 바뀌어 API가 망가지는 것을 방지할 수 있다.
  1. keyword-only를 쓰면 좋은 상황
    1. parameter name에 어떤 의미가 있다.
    2. user가 position으로 argument를 전달하게 하고 싶지 않다.
    3. 이름을 둬서 함수 정의를 보다 명료하게 하고 싶다.

4.5. Unpacking Argument: *args, **kwargs

똑같은 *** 기호를 사용하기 때문에 Arbitrary Argument: *args, **kwargs 부분과 헷갈릴 수 있다. 명료하게 하자면 이전까지는 함수를 정의할 때 *를 사용한 것이고 이 부분은 함수를 호출할 때 *를 쓰는 것이다.
argument가 이미 list, tuple, dictionary로 되어 있을 때 unpack해서 함수를 호출하고 싶다면 list 또는 tuple일 때는 *를 사용하고 dictionary일 때는 **를 통해 unpack할 수 있다. (dictionary에서 *를 사용하면 key만 전달되고 keyword argument가 아니라 positional argument로 작동한다.)
def f(arg1, arg2, arg3): print(arg1, arg2, arg3) args = (1, 2, 3) f(*args)
page icon
1 2 3
def f(arg1, arg2, arg3): print(arg1, arg2, arg3) args = {'arg1': 1, 'arg2': 2, 'arg3': 3} f(**args)
page icon
1 2 3
 

5. Lambda Expressions

이름이 없고 간단한 함수를 lambda keyword로 만들 수 있다. lambda {argument list}: {return value} 와 같은 형식으로 쓴다. 이때 아래와 같은 특징이 있다.
  • Lambda 함수는 일반적인 def function으로 완전히 대체 가능하다. 단순히 좀 더 간결히 쓸 수 있는 것만이 존재 이유이다.
  • 본문을 한 줄만 쓸 수 있도록 제한 돼 있다. 여러 줄이 필요하면 일반 def 함수를 쓰자.
  • 일반 def 함수처럼 Lambda 함수도 자신을 감싸는 함수의 variable을 참조할 수 있다.
예를 들어 아래와 같이 두 값을 더하는 함수를 만드는함수를 보자. lambda 함수를 감싸는 make_incrementor 함수의 n을 참조할 수 있는 모습이다. 이처럼 함수를 return할 때 lambda를 유용하게 쓸 수 있다.
def make_incrementor(n): return lambda x: x + n f = make_incrementor(42) f(10)
page icon
52
다른 예시로는 함수를 argument로 전달할 때도 쓸 수 있다. list.sort()는 sorting key function을 필요로 하는데 이 key를 lambda 함수로 전달할 수 있다.
pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')] pairs.sort(key=lambda pair: pair[1]) pairs
page icon
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
또 자주 쓰는 예시는 아래와 같다. pandas dataframe에서 table을 불러올 때 여러 column의 converters를 정의하거나 dataframe을 이미 불러오고 나서 각 행마다의 변환을 쉽게 할 수 있다.
converters = { 'distance': lambda x: float(x) / 1_000, 'time' : lambda x: float(x) / 3_600, } df = pd.read_csv(path, converters=converters)
df['lat_lng'] = df.apply(lambda row: (df['lat'], df['lng']), axis=1)
참고로 위 코드에서 axis=1로 해야 row들에 적용하고 axis=0으로 하면 column들에 적용한다.

6. Documentation Strings

6.1. 정의

함수 Body의 첫 줄에 string literal이 올 때 이 string literal을 documentation string 또는 docstring이라고 한다. doctring은 ide이나 다른 프로그램에서 해당 함수에 대한 설명을 읽어올 때 관습적으로 사용된다.
string literal이란 ‘로 감싸거나 “로 감싸거나 “””로 감싼 텍스트를 말한다. “””로 감싸면 line break도 기록되기 때문에 주로 “””로 감싼다.

6.2. 관습

  1. 첫 번째 줄은 함수의 목적을 짧게 요약하고 대문자로 시작한다. 첫 번째 줄에서 type을 명시하진 않는다.
  1. 더 많은 설명이 필요하면 두 번째 줄은 비워둬서 요약과 상세 설명을 시각적으로 구분시킨다.
  1. 그 이후부터는 호출 방식, 각 argument의 type 등 다양한 내용을 담는다.
 
이때 여러 줄에 걸쳐 작성한다면 docstring의 identation은 첫 번째 줄 그 다음에 오는 비어 있지 않은 첫 줄의 identation으로 결정된다. 즉, Do noting,…이 첫 번째 줄이고 그 다음으로 non blank인 줄은 No, …이다. 이 줄의 Identation을 doctring 전체의 identation으로 설정한다.
def my_function(): """Do nothing, but document it. No, really, it doesn't do anything. """ pass print(my_function.__doc__)
page icon
Do nothing, but document it. No, really, it doesn't do anything.

6.3. Annotations

Annotation은 type에 대한 메타 데이터로 선택적으로 추가할 수 있고 함수 자체에는 아무런 변화도 가져오지 않는다. argument와 return 값의 type을 보다 구체적으로 명시하고 싶을 때 사용한다. 명시하면 dictionary 형태로 함수의 __annotations__ 속성에 저장된다.
  1. Parameter annotation은 parameter 이름 다음에 : str, int, float과 같은 expression을 남긴다. 아래 예시에서 = 'eggs' 는 default argument value로 annotation과는 별개이다.
  1. Return annotation은 parameter list와 def 선언의 : 사이에 -> 과 expression을 적어 정의한다.
def f(ham: str, eggs: str = 'eggs') -> str: print("Annotations:", f.__annotations__) print("Arguments:", ham, eggs) return ham + ' and ' + eggs f('spam')
page icon
Annotations: {'ham': <class 'str'>, 'return': <class 'str'>, 'eggs': <class 'str'>} Arguments: spam eggs 'spam and eggs'
 

7. Coding Style

길고 복잡한 코드를 작성할 때 보다 간결하고 format을 맞춰 적는 것은 다른 사람이나 몇 달 뒤 내가 코드를 볼 때 더 잘 이해할 수 있다. PEP 8 에는 Python 개발자가 언젠가는 반드시 읽어야 하는 스타일 가이드가 쓰여 있다. 그중 가장 중요한 몇 가지는 아래와 같다.
 
  • 4-space identation(들여쓰기)를 하라. tab을 쓰지 말라.
    • 그보다 적은 space는 더 깊은 nesting을 할 수 있지만 읽기 어렵고 그보다 많아도 역시 코드를 읽기 어려워진다. tab 문자는 혼동을 일으킨다.
      (최신 IDE에서는 tab만으로 4-space identation이 자동으로 되기는 한다.)
  • 79 글자를 넘지 않도록 줄바꿈을 하라.
    • side-by-side 화면 등에서 보다 코드가 잘 보인다.
  • 함수, class, 함수 내의 큰 덩어리의 코드를 분리하기 위해 blank lines를 쓰라.
  • 가능하다면 주석은 코드 옆에다 쓰지 말고 주석줄만 따로 둬서 써라.
  • docstrings을 써라.
  • Operator와 comma 다음에는 space를 둬라.
    • +, -, *, /, = 와 같은 operator 양 옆에 space
    • comma 다음에 space
    • () , {} , [] 와 같은 괄호에서는 바로 space를 두지 말라. ✅(1, 2)( 1, 2 )
    • 예시: a = f(1, 2) + g(3, 4)
  • class와 function의 이름을 일관적으로 작명해라.
    • class는 UpperCamelCase 을 사용하는 것이 관례이고,
    • function이나 method는 lowercase_with_underscores 를 쓰는 것이 관례이다.
  • class의 첫 번째 method는 항상 이름을 self로 둬라.
  • 여러 언어의 환경에서 사용된다면 encoding을 복잡하게 두지 말라. Python의 기본 encoding은 UTF-8이다. 심지어 ASCII 로만으로도 어느 상황에서든 잘 작성할 수 있다. 웬만하면 ASCII 문자로만 코드를 작성해라. 다른 언어의 개발자가 읽거나 유지 보수를 해야할 수도 있다.
 

Reference

[1] Python Software Foundation. "4.8. Special Parameters." The Python Tutorial, version 3.13, Python Software Foundation, 2024, docs.python.org/3.13/tutorial/controlflow.html#special-parameters. Accessed 10 July 2025.