위치 인자 (Positional argument) 를 가변적으로 받을 수 있으면 함수 호출이 더 깔끔해지고 시각적 잡음도 줄어든다.
def log(message, values):
if not values:
print(message)
else:
values_str = ', '.join(str(x) for x in values)
print(f'{message}: {values_str}')
log('내 숫자는', [1, 2])
내 숫자는: 1, 2
log('안녕', [])
안녕
빈 리스트를 넘겨야 한다면 귀찮음 뿐만 아니라 잡음이 많다.
생략 가능하도록 하자
def log(message, *values):
if not values:
print(message)
else:
values_str = ', '.join(str(x) for x in values)
print(f'{message}: {values_str}')
log('내 숫자는', 1, 2)
내 숫자는: 1, 2
log('안녕')
안녕
언패킹 대입문에 쓰인 별표식과 비슷함
시퀀스를 사용하고 싶다면 * 연산자를 사용하면 된다.
favorite = [1, 2, 3]
log('내 숫자는', *favorite)
내 숫자는: 1, 2, 3
favorite = [1, 2, 3]
log('내 숫자는', favorite)
내 숫자는: [1, 2, 3]
가변적인 위치 인자를 받는 데는 두 가지 문제점이 있다.
첫번째 : 선택적인 위치 인자가 함수에 전달되기 전에 항상 튜플로 변환됨
메모리를 아주 많이 소비하거나 프로그램이 중단될 수 있음.
def my_generator():
for i in range(10):
yield i
def my_func(*args):
print(args)
it = my_generator()
my_func(*it)
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
두 번째 문제점은 새로운 위치 인자를 추가하면 해당 함수를 호출하는 모든 코드를 변경해야 한다.
def log(sequence, message, *values):
if not values:
print(f'{sequence} - {message}')
else:
values_str = ', '.join(str(x) for x in values)
print(f'{sequence} - {message}: {values_str}')
log(1, '좋아하는 숫자는', 7, 33)
1 - 좋아하는 숫자는: 7, 33
log(1, '안녕')
1 - 안녕
log('좋아하는 숫자는', 7, 33)
좋아하는 숫자는 - 7: 33
이런 가능성을 완전히 없애려면 *args를 받아들이는 함수를 확장할 떄는 키워드 기반의 인자만 사용해야 한다. (Better way 25)
더 방어적으로 프로그래밍하려면 타입 애너테이션 (Better way 90)을 사용해도 된다.