안녕하세요. 글쓰는 개발자 입니다.
Python 개발을 하다보면 종종 보이는 것이 있습니다. 바로, 흔히들 별표라고 부르는 asterisk, * 입니다. Python 개발자라면 누구나 익히 알고 있듯이 곱셉 연산, unpacking 등에 사용되는 것은 물론, *args / **kwargs와 같이 positional / keword arguments 역할도 할 수 있습니다.
그런데 최근 단순 궁금증으로 FastAPI의 APIRouter 구현을 뜯어보던 중 FastAPI에 작성된 mutiple arguments를 받는 method들은 대부분 arguments로 asterisk (*)를 포함하고 있었습니다.
'그림1'과 같이 fastapi/routing.py 파일의 APIRouter Class의 add_api_route method를 살펴보면 알 수 있듯이 self, path, endpoint 이후에 한 개의 asterisk가 나타나는 것을 볼 수 있습니다. 타고 넘어가다 보니 FastAPI가 base로 사용한 Starlette 소스코드까지 넘어가게 되었는데 Starlette 전체를 확인하지는 않았지만 잘 찾을 수 없었습니다. (그림2 참고)
modern Python 문법을 지향하는 FastAPI의 철학을 바탕으로 유추했을 때 가독성 향상 또는 Type Hint와 같이 Bug 발생을 줄이기 위한 목적이 있을 것이라 생각하고 찾아보던 중 PEP 3102 에 "Keyworkd only Arguments"로 명시되어 있음을 확인했습니다.
PEP-3102 Keyword Only Arguments
motivation
postional arguments에 의해 자동으로 채워지지 않을 keyword only arguments 선언을 위해 제안합니다. 현재, python의 method 호출 패러다임은 명시적 keyword와 위치로 arguments를 전달할 수 있습니다. Python은 '남겨진' 인수를 tuple로 varargs 매개 변수에 전달하도록 지정하는 'varargs' 구문(*name)을 사용하여 이를 지원 합니다.
익히 알고 있는 *args, **kwargs에는 몇 가지 문제가 있습니다. *args의 경우에는 개수의 제한 없이 postional arguments를 전달 할 수 있다는 장점이 있지만 tuple로 확인해야하고 **kwagrs는 keyword로 전달하지만 keyword 네이밍에 제한이 없다는문제가 있습니다. 그리고 많은 사람들이 아래 toggle_activate를 보고 이런 오해를 하고 있습니다.
'toggle_activate method에서 user_id는 postional argument 이고, is_activate는 keyword argument 이구나.'
def toggle_activate(user_id: str, is_activle: bool=True):
pass
# 다양한 method 호출 방식
toggle_activate('test_id)
toggle_activate('test_id', False)
toggle_activate(user_id='test_id')
toggle_activate(user_id='test_id', is_active=False)
toggle_activate(is_active=False, user_id='test_id')
하지만 위 '다양한 method 호출 방식'을 보면 알 수 있듯이 사실은 둘다 positional 이나 keword arguments로써의 역할을 할 수 있습니다. method의 arguments에 default value가 있는 is_active 값을 변경 할 때 keyword를 명시하다 보니 이런 오해가 종종 발생하고는 합니다.
그리고 이런 오해 때문에 개발자는 method를 만들면서 호출시점에 모두 postional arguments로 할 지? 무엇을 keword arguments로 정해야할지? 어떤 것을 강제해야 할 지? 그리고 keyword arguments로 표시하기 위해 default value는 무엇으로 정하는 것이 좋을지? 와 같은 고민에 빠지게 됩니다. 이러한 고민 해결을 위해 Keyword Only Arguments 도입이 필요합니다.
Keyword Only Arguments
사용 방법은 간단합니다. asterisk (*) 를 method의 arguments로 전달 함으로써, positional arguments 와 keyword only arguments 를 split하는 역할을 합니다. 아래 소스코드를 보시면, asterisk (*) 를 기준으로 text는 positional arguments로 인식되고 뒤에 오는 num, flt 는 keworkd only arguments로써 method 호출 시 반드시 keyword를 명시해야 호출이 가능합니다.
def bare_asterisk(text: str, *, num: int, flt: float):
pass
def call_bare_asterisk():
bare_asterisk('text', num=1, flt=1.1)
bare_asterisk('text', 1, 1.1) # Unexpected Arguments Error!!!
결론
우연히 발견하게 된 문법이라 modern python, python 최신 문법 이런 것과 관련 있을 줄 알았는데 의외로 Python 3.0 부터 PEP-3102에 명시되어 있었다고 합니다. 적극적으로 사용해서 견고하고 Bug 발생을 줄이는 코드를 작성하는 데 도움되도록 해야겠습니다.
참고
https://peps.python.org/pep-3102/
https://lukeplant.me.uk/blog/posts/keyword-only-arguments-in-python/
'Python' 카테고리의 다른 글
Python decorator(파이썬 데코레이터) functools.wraps 사용하기 / 디버깅 가능한 데코레이터 작성 (0) | 2023.03.08 |
---|---|
FastAPI, Django, Django REST Framework, Flask 비교 - 어떤 프레임워크를 선택해야 할까? (0) | 2023.03.01 |
Python Class Decorator 인자 값 전달 (Django, DRF, 권한 검증) (2) | 2022.11.22 |