키워드를 사용해 인자를 넘기는 기능은 파이선 함수의 강력한 기능중 하나에요.
키워드 인자를 활용하면 코드의 가독성이 매우 높아집니다. 다른말로는 코드의 품질이 높아져요.
예를 들어 한 숫자를 다른 숫자로 나눌 때 예외적으로 발생하는 오류를 처리 방법을 볼게요.
ZeroDivisionError
OverflowError
위 두가지 오류처리에서 ZeroDivisionError 예외를 무시하고 무한대를 반호나하고 싶고 어떠한 경우에는 OverflowError 오류를 무시하고 0을 반환하고자 하는 경우가 있어요.
def safe_division(number, divisor, ignore_overflow, ignore_zero_division):
try:
return number / divisor
except OverflowError:
if ignore_overflow:
return 0
else:
raise
except ZeroDivisionError:
if ignore_zero_division:
return float('inf')
else:
raise
아래는 위 정의한 함수를 호출하는 예시에요.
result = safe_division(1.0, 10**500, True, False)
print(result)
>>>
0
result = save_divisioin(1,0, 0, False, True)
print(result)
>>>
inf
문제는 어떤 예외를 무시할지 결정하는 두 bool 변수의 위치를 혼동하기 쉽다는 것이조. 그로인해 bug가 발생할수도 있고요.
방법은 코드 가독성을 높이는 것이며 그중 키워드 인자를 사용하는 것이에요
def save_division(number,
divisior,
ignore_overflow=False,
ignore_zero_division=False):
...
함수 호출시 키워드 인자를 사용해 무시할 예외를 설정하는 flag를 이용할 수 있어요.
result = save_divisioin(1,0, 10**500, ignore_over_flow=True)
print(result)
result = save_divisioin(1,0, 0, ignore_zero_division=True)
print(result)
>>>
0
inf
위와 같은 방식으로 키워드 인자를 사용하는 것이 선택적인 사항이므로 호출 하는 쪽에서 명확성을 위해 키워드 인자를 꼭 쓰도록 강요할 수 없다는데 있습니다. 새로 정의한 safe_division_b에서도 여전히 위치 인자를 통해 예전 방식으로 함수를 호출 할 수 있습니다.
assert safe_division_b(1.0, 10**500, True, False) == 0
이와 같이 복잡한 함수의 경우 호출자가 키워드만 사용하는 인자를 통해 의도를 명확히 밝히도록 요구하는 편이 좋다. 키워드만 사용하는 인자는 키워드를 반드시 사용해 지정해야 하며, 절대 위치를 기반으로는 지정할 수 없다.
def save_division_c(number, divisior, *,
ignore_overflow=False,
ignore_zero_division=False):
...
이제 이 함수를 호출하면서 키워드 인자를 써야 하는데, 위치 인자를 사용하면 프로그램이 제대로 작동하지 않는다.
safe_division_c(1.0, 10**500, True, False)
>>>
Traceback...
TypeError: safe_division_c() take 2 positional arguments but 4 were given
하지만 키워드 인자와 디폴트 값은 예상대로(아래 예에서 하나의 경우는 예외를 무시하고, 다른 경우는 예외를 던진다) 잘 작동함
result = safe_division_c(1.0, 0, ignore_zero_division=True)
assert result == float('inf')
try:
result = safe_division_c(1.0, 0)
except ZerodivisionError:
pass #예상대로 작동함
하지만 이 safe_division_c 함수에도 문제가 있어요. 호출하는 쪽에서 이 함수의 맨 앞에 있는 두 필수 인자(number=2, divisor=5)를 호출하면서 위치와 키워드를 혼용 할 수 있다.
assert safe_division_c(number=2, divisor=5) == 0.4
assert safe_division_c(divisor=5, number=2, ) == 0.4
assert safe_division_c(2, divisor=5) == 0.4
추후 요구 사항이 바뀌거나 원하는 스타일이 바뀌어서 맨 앞의 두 인자 이름이 변경 될 수도 있어요.
def save_division_c(numerator, denominator, *,
ignore_overflow=False,
ignore_zero_division=False):
...
이런 사소한 변경만으로도 number와 divisor 인자를 키워드로 호출하는 기존 호출 코드는 쉽게 깨져 버리게 되요.
safe_division_c(number=2, divisor=5)
>>>
Traceback...
TypeError: safe_division_c() got an unexpected keyword argument
'number'
이 상황에서는 또 다른 문제가 야기 되는데, 함수의 명시적 인터페이스에 number와 divisor를 포함시키는 것을 의도하지 않았기 때문에 일어 나네요.
파이썬 3.8에서는 이 문제를 해결할 방법을 제시하여 줍니다.
말 그대로 위초로만 지정하는 인자는 반드시 위치만 사용해 인자를 지정해야하고 키워드 인자로는 쓸 수 없어요.
def save_division_c(number, divisior, ,/ , *,
ignore_overflow=False,
ignore_zero_division=False):
...
필수 인자를 위치인자로 지정할 제대로 작동하는지 아래코드를 통해 확인해볼게요.
assert safe_division_d(2, 5) == 0.4
하지만 필수 인자를 위치로만 지정하는 인자를 키워드를 사용해 지정하면 예외가 발생해요.
assert safe_division_d(numerator=2, denominator=5) == 0.4
>>>
Traceback...
TypeError: safe_division_c() got some positional-only arguments
passed as keyword arguments: 'numerator, denominator'
이제 safe_division_d 함수 정의에서 맨 앞의 두 필수 인자는 호출하는 쪽과 분리 되었어요. 따라서 파라미터 이름을 바꾸어도 망가지지 않아요.
인자목록에서 / 와 * 기호사이에 있는 모든 파라미터는
결국 구현하고자 하는 API의 스타일이나 필요에 따라 두 인자 전달 방식을 모두 사용하면 가독성을 높이고 잡음도 줄일수 있어요.
예를 들어 다음 코드에서는 결과를 표시 할 때 얼마나 많은 자릿수를 사용할 지 결정하고자 반올림할 위치를 지정하는 인자를 safe_division에 추가한다고 해볼게요.
def safe_division(numerator, denominator, /,
ndigits=10, *,
ignore_overflow=False,
ignore_zero_division=False):
try:
fraction = numerator / denominator
return round(fraction, ndigits)
except OverflowError:
if ignore_overflow:
return 0
else:
raise
except ZeroDivisionError:
if ignore_zero_division:
return float('inf')
else:
raise
위 정의된 함수에서 ndigits는 위치나 키워드를 사용해 전달할 수 있는 선택적인 파라미터이므로 어떤 방식으로든 함수 호출에 사용할 수 있어요.
result = safe_division_e(22, 7)
print(result)
result = safe_division_e(22, 7, 5)
print(result)
result = safe_division_e(22, 7, ndigits=2)
print(result)
>>>
3.1428571429
3.14286
3.14
keyword-only 인자를 사용하면 호출하는 쪽에서 특정 인자를(위치를 사용하지 않고) 반드시 키워드를 사용해 호출하도록 강제할 수 있다. 이로 인해 함수 호출의 의도를 명확히 할 수 있다. keyword-only는 인자 목록에서 * 다음에 위치한다.
positional-only 인자를 사용하면 호출하는 쪽에서 키워드를 사용해 인자를 지정하지 못하게 만들 수 있고 이에 따라 함수 구현과 함수 호출 지점 사이의 결합을 줄일 수 있다. positional-only는 함수 선언부의 인자 나열 목록에서 / 기호 앞에 위치 시킨다.
인자 나열 목록에서 /와 * 사이에 있는 파라미터는 키워드를 사용해 전달해도 되고 위치를 기반으로 전달해도 된다. 이런 동작은 파이썬 함수 파라미터의 기본 동작이다.