[Python] 함수 기초: def, 인수, return
python function, python def, args kwargs, 함수 인수, positional keyword
def
def 문으로 함수를 정의한다. 함수도 **일급 객체(first-class)**라 변수에 할당하고 인자로 전달할 수 있다.
def add(a, b):
"""두 수의 합 반환."""
return a + b
f = add # 함수 객체 참조
print(f(3, 4)) # 7
print(add.__doc__) # 두 수의 합 반환.
print(add.__name__) # add
인수 종류
Python 함수 시그니처는 6가지 매개변수 종류를 조합한다.
def f(pos_only, /, pos_or_kw, *args, kw_only, **kwargs):
...
| 위치 | 종류 | 설명 |
|---|---|---|
pos_only 앞 | positional-only (3.8+) | / 앞: 키워드로 못 부름 |
/ 와 * 사이 | positional-or-keyword | 둘 다 가능 (기본) |
*args | var-positional | 가변 위치 인수 → tuple |
* 또는 *args 뒤 | keyword-only | 키워드로만 부를 수 있음 |
**kwargs | var-keyword | 가변 키워드 → dict |
python
def demo(a, b, /, c, d, *args, e, f=10, **kwargs):
print(f"a={a}, b={b}, c={c}, d={d}")
print(f"args={args}, e={e}, f={f}, kwargs={kwargs}")
demo(1, 2, 3, d=4, e=5, x=99, y=100) 결과
a=1, b=2, c=3, d=4
args=(), e=5, f=10, kwargs={'x': 99, 'y': 100}기본값 인수
def greet(name, greeting="Hello"):
return f"{greeting}, {name}"
greet("Alice") # "Hello, Alice"
greet("Bob", "Hi") # "Hi, Bob"
greet("Carol", greeting="Hey") # "Hey, Carol"
주의: 기본값은 한 번만 평가 (가변 기본값 함정)
python
def append_bad(item, items=[]): # WRONG
items.append(item)
return items
print(append_bad(1)) # [1]
print(append_bad(2)) # [1, 2] (공유됨!)
def append_good(item, items=None):
if items is None:
items = []
items.append(item)
return items
print(append_good(1)) # [1]
print(append_good(2)) # [2] (매번 새로) 결과
[1]
[1, 2]
[1]
[2]기본값 표현식은 def가 평가될 때 단 한 번 계산된다. 가변 객체(list, dict)는 절대 기본값으로 쓰지 말 것. 관용구: None 센티넬.
*args, **kwargs
가변 인수 수집·전달에 사용.
python
def variadic(*args, **kwargs):
print(args, kwargs)
variadic(1, 2, 3, x=10, y=20)
# 호출 시 언패킹
def add3(a, b, c):
return a + b + c
nums = [1, 2, 3]
print(add3(*nums)) # 1, 2, 3
params = {"a": 1, "b": 2, "c": 3}
print(add3(**params)) 결과
(1, 2, 3) {'x': 10, 'y': 20}
6
6함수 전달 시 그대로 통과시키는 패턴 (decorator, wrapper):
def wrapper(*args, **kwargs):
log.info("called", args=args, kwargs=kwargs)
return inner(*args, **kwargs)
keyword-only 인수
*args 뒤(또는 단독 * 뒤)에 오는 인수는 키워드로만 호출 가능.
def connect(host, port, *, timeout=30, retries=3):
...
connect("localhost", 8080) # OK
connect("localhost", 8080, timeout=60) # OK
connect("localhost", 8080, 60) # TypeError
API에 옵션이 많을 때 잘못된 위치 인수 전달을 방지한다.
positional-only 인수 (3.8+, PEP 570)
/ 앞 매개변수는 위치로만 호출 가능. 키워드 이름을 API 계약에서 빼고 싶을 때 유용.
def divmod_(a, b, /):
return a // b, a % b
divmod_(10, 3) # OK
divmod_(a=10, b=3) # TypeError
내장 함수의 동작과 일치시키거나, 향후 이름 변경에 자유롭게 하기 위함.
return
명시적 return 없으면 None 반환. 다중 값은 튜플로.
def parse(line):
parts = line.split()
if not parts:
return None # 명시적 조기 반환
return parts[0], int(parts[1]) # 튜플 (다중값)
key, value = parse("count 42")
함수 어노테이션 (타입 힌트)
def greet(name: str, age: int = 0) -> str:
return f"Hello {name}, {age}"
# 어노테이션 조회
print(greet.__annotations__)
# {'name': <class 'str'>, 'age': <class 'int'>, 'return': <class 'str'>}
런타임에 강제하지 않지만 mypy, pyright 같은 타입 체커가 활용. 자세한 건 py-type-hints 참고.
람다 (anonymous function)
단일 표현식만 가능한 익명 함수.
square = lambda x: x ** 2
print(square(5)) # 25
# 정렬 키
words = ["apple", "pie", "banana"]
words.sort(key=lambda s: len(s))
# map/filter
squared = list(map(lambda x: x ** 2, range(5)))
복잡한 람다보다 def로 분리하는 게 가독성 좋음.
일급 함수와 클로저
함수는 객체라서 다른 함수에 전달, 반환, 변수 할당 가능.
def make_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
counter = make_counter()
print(counter(), counter(), counter()) # 1 2 3
nonlocal 없이는 count에 재할당하면 새 로컬 변수가 생긴다.
데코레이터 (간단 소개)
함수를 받아 함수를 반환하는 함수.
def log(fn):
def wrapper(*args, **kwargs):
print(f"calling {fn.__name__}")
return fn(*args, **kwargs)
return wrapper
@log
def add(a, b):
return a + b
# 동등: add = log(add)
add(1, 2) # "calling add" 출력 후 3
상세는 py-decorator 페이지에서.
💬 댓글