학습 목표
def로 함수를 정의하고 기본 인자·키워드 인자를 JS 대응으로 사용할 수 있다*args/**kwargs로 가변 인자를 받아 rest/spread 습관을 Python으로 옮길 수 있다lambda의 "한 줄 표현식" 제약을 이해하고 언제 def를 써야 할지 판단할 수 있다- 가변 기본값(
def f(x=[])) 공유 함정을 피하는None관용구를 쓸 수 있다
JS에서 함수는 값이자 일급 객체입니다. Python도 똑같습니다. 다만 화살표 함수 같은 간결 문법이 없고, 모든 함수는 def 키워드로 정의합니다.
| JavaScript | Python |
|---|---|
const add = (a, b) => a + b;const r = add(2, 3); |
def add(a, b): return a + br = add(2, 3) |
가장 큰 차이는 세 가지입니다. (1) 화살표 함수의 암묵적 return이 없어 값을 돌려주려면 항상 return을 씁니다. (2) 블록은 중괄호가 아니라 들여쓰기입니다. (3) 함수도 값이므로 변수에 넣거나 인자로 넘길 수 있습니다.
def add(a, b):
return a + b
def apply(func, x, y):
return func(x, y)
print(apply(add, 2, 3))
# 출력: 5return이 없으면 JS의 undefined가 아니라 None을 반환합니다.
def greet(name):
print(f"안녕하세요, {name}님")
result = greet("Ada")
print(result)
# 출력: 안녕하세요, Ada님JS의 기본 매개변수는 Python에도 있습니다. 여기에 더해 Python은 호출 시 인자 이름을 지정하는 키워드 인자를 언어 차원에서 지원합니다.
| JavaScript | Python |
|---|---|
function f(a, b = 10) {}f(1); |
def f(a, b=10): ...f(1) |
키워드 인자를 쓰면 인자 순서와 무관하게, 이름으로 값을 전달할 수 있습니다. 가독성이 크게 올라가므로 인자가 3개 이상인 함수 호출에서 특히 유용합니다.
def connect(host, port=5432, timeout=30):
return f"{host}:{port} (timeout={timeout})"
print(connect("db.local"))
# 출력: db.local:5432 (timeout=30)
print(connect("db.local", timeout=5))
# 출력: db.local:5432 (timeout=5)
print(connect("db.local", port=3306, timeout=5))
# 출력: db.local:3306 (timeout=5)🎯 AICE: sklearn·keras API는 키워드 인자로 도배되어 있습니다.
train_test_split(X, y, test_size=0.2, random_state=42),model.fit(X, y, epochs=10, batch_size=32)처럼 대부분의 하이퍼파라미터가 키워드 인자입니다. 빈칸 채우기 문제에서random_state=____형태가 흔하므로 이름과 순서에 익숙해져야 합니다.
JS의 rest 파라미터 ...args는 Python에서 두 갈래로 나뉩니다. 위치 인자를 모으는 *args(튜플)와 키워드 인자를 모으는 **kwargs(딕셔너리)입니다.
| JavaScript | Python |
|---|---|
function sum(...nums) {} |
def total(*nums): |
f(...arr) (spread 호출) |
f(*arr) (언패킹 호출) |
*args는 남는 위치 인자를 튜플로 모읍니다. 호출할 때 리스트 앞에 *를 붙이면 반대로 펼쳐서 넘길 수 있습니다 — 이것이 JS spread의 대응입니다.
def total(*nums):
return sum(nums)
print(total(1, 2, 3, 4))
# 출력: 10
values = [10, 20, 30]
print(total(*values))
# 출력: 60**kwargs는 이름 붙은 인자를 딕셔너리로 모읍니다. 설정값을 유연하게 받는 함수에서 자주 씁니다.
def make_user(**kwargs):
return kwargs
print(make_user(name="Ada", role="admin"))
# 출력: {'name': 'Ada', 'role': 'admin'}
config = {"host": "db.local", "port": 3306, "timeout": 5}
print(connect(**config))
# 출력: db.local:3306 (timeout=5)딕셔너리 앞에 **를 붙이면 키워드 인자로 펼쳐집니다. 위에서 connect(**config)는 connect(host="db.local", port=3306, timeout=5)와 같습니다.
JS 화살표 함수와 가장 닮은 것은 lambda입니다. 그러나 결정적 제약이 있습니다. lambda 본문은 단 하나의 표현식이어야 합니다. 문(statement)을 넣을 수 없으니 if문, 반복문, 여러 줄을 담지 못합니다.
| JavaScript | Python |
|---|---|
const sq = x => x * x; |
sq = lambda x: x * x |
arr.sort((a,b) => a - b) |
sorted(arr, key=lambda x: x) |
lambda는 주로 sorted, map, filter의 key/변환 함수처럼 "잠깐 쓰고 버릴" 자리에서 씁니다. 로직이 조금이라도 길어지면 미련 없이 def로 바꾸는 것이 Python다운 선택입니다.
people = [("Ada", 36), ("Bob", 28), ("Cid", 41)]
by_age = sorted(people, key=lambda person: person[1])
print(by_age)
# 출력: [('Bob', 28), ('Ada', 36), ('Cid', 41)]
⚠️ JS 함정: JS에서는 화살표 함수 본문에{ ... }로 여러 문장을 쓰지만, Pythonlambda는 그게 불가능합니다.lambda x: (print(x), x*2)처럼 억지로 쑤셔 넣지 말고, 두 줄 이상이면def를 쓰세요. 삼항 표현식(a if cond else b)은 표현식이므로 lambda 안에서 사용할 수 있습니다.
JS에는 없는, Python 초심자를 반드시 한 번은 무너뜨리는 함정입니다. 기본 인자값은 함수가 정의될 때 딱 한 번만 평가되어, 모든 호출이 같은 객체를 공유합니다. 리스트나 딕셔너리를 기본값으로 쓰면 호출 간에 상태가 새어 나갑니다.
def bad_append(item, target=[]): # target이 모든 호출에서 공유됨
target.append(item)
return target
print(bad_append(1))
# 출력: [1]
print(bad_append(2))
# 출력: [1, 2]두 번째 호출에서 [2]가 아니라 [1, 2]가 나옵니다. 빈 리스트가 매번 새로 생기는 게 아니기 때문입니다. JS의 function f(x, t = [])는 호출마다 새 배열을 만들지만, Python은 그렇지 않습니다.
⚠️ JS 함정: 가변 객체(list·dict·set)를 기본 인자로 절대 쓰지 마세요. JS 습관대로def f(x=[])라고 쓰면 상태가 호출 간에 공유되어 디버깅이 극도로 어려운 버그가 됩니다.
해결책은 기본값을 None으로 두고 함수 안에서 새로 만드는 관용구입니다. 이 패턴을 손에 익히세요.
def good_append(item, target=None):
if target is None:
target = []
target.append(item)
return target
print(good_append(1))
# 출력: [1]
print(good_append(2))
# 출력: [2]이제 호출마다 독립된 리스트가 생깁니다. is None으로 검사하는 이유는 2장에서 다룬 정체성 비교 관용구입니다.
Q1. 임의 개수의 숫자를 받아 평균을 반환하는 함수입니다. 빈칸을 채우세요.
def average(____nums):
return sum(nums) / len(nums)
print(average(10, 20, 30)) # 20.0정답
def average(*nums):
return sum(nums) / len(nums)
print(average(10, 20, 30))
# 출력: 20.0*nums가 위치 인자를 튜플로 모읍니다.
Q2. 가변 기본값 함정을 피하도록 빈칸을 채우세요.
def collect(value, bucket=____):
if bucket is None:
bucket = []
bucket.append(value)
return bucket정답
def collect(value, bucket=None):
if bucket is None:
bucket = []
bucket.append(value)
return bucket
print(collect(1))
# 출력: [1]기본값을 None으로 두고 함수 내부에서 새 리스트를 생성해 호출 간 공유를 막습니다.
Q3. 딕셔너리 opts를 키워드 인자로 펼쳐 connect에 전달하는 코드입니다. 빈칸을 채우세요.
def connect(host, port=5432):
return f"{host}:{port}"
opts = {"host": "srv", "port": 8080}
print(connect(____opts)) # srv:8080정답
def connect(host, port=5432):
return f"{host}:{port}"
opts = {"host": "srv", "port": 8080}
print(connect(**opts))
# 출력: srv:8080**가 딕셔너리를 키워드 인자로 펼칩니다.
| JS | Python | 비고 |
|---|---|---|
(a, b) => a + b |
def f(a, b): return a + b |
암묵 return 없음 |
x => x * x |
lambda x: x * x |
본문은 단일 표현식만 |
기본 매개변수 b = 10 |
b=10 |
가변 객체 금지 |
rest ...args |
*args(튜플) |
위치 인자 모으기 |
| (이름 인자 모으기 없음) | **kwargs(dict) |
키워드 인자 모으기 |
spread 호출 f(...arr) |
f(*arr) / f(**d) |
언패킹 호출 |
| (함정 없음) | def f(x=[]) 공유 |
None 기본값 관용구 필수 |