변수의 타입을 지정하는 것처럼 보이지만 실제로는 주석

따라서 변수의 타입이 무엇이 되든 영향을 받지 않음


In [1]:
def anno_func(arg1: str, arg2: 'also str', arg3: 1 is True) -> bool:
    print(arg1, arg2, arg3)

In [2]:
anno_func(1, True, 'world')


1 True world

__annotations__에는 주석으로 지정한 값이 할당되어 있음


In [3]:
print(anno_func.__annotations__)


{'arg1': <class 'str'>, 'arg2': 'also str', 'arg3': False, 'return': <class 'bool'>}

In [4]:
print(anno_func.__annotations__['arg1'])


<class 'str'>

In [5]:
def static_args_func(arg1: str, arg2: str, arg3: int) -> bool:
    args = locals() # 현재의 namespace를 딕셔너리로 구성하여 반환
#     print(args)
    for _k, _v in args.items():
        arg_type = static_args_func.__annotations__[_k]

        if isinstance(_v, arg_type): # isinstance()함수는 해당 값의 자료형태를 비교할때 사용
            continue

        raise TypeError(
            "The type of '{}' does not match '{}' type".format(
                _k, arg_type.__name__
            )
        )
    print(arg1, arg2, arg3)

In [6]:
static_args_func(1, 2, 3)


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-021e4ceb3481> in <module>()
----> 1 static_args_func(1, 2, 3)

<ipython-input-5-ded5faf8596e> in static_args_func(arg1, arg2, arg3)
     10         raise TypeError(
     11             "The type of '{}' does not match '{}' type".format(
---> 12                 _k, arg_type.__name__
     13             )
     14         )

TypeError: The type of 'arg2' does not match 'str' type

가변 매개 변수 형 검사


In [7]:
def type_checking_func(*args: (int, int, ...)):
    # 두번째 변수까지 형 검사 실행, 그 이후 변수는 형 검사 생략
    annotations = type_checking_func.__annotations__

    if (
        not isinstance(annotations, dict) or
        len(annotations) == 0
    ):
        return type_checking_func(*args)

    try:
        _check_index = annotations['args'].index(Ellipsis)
    except ValueError:
        _check_index = len(annotations) - 1

    for i, _v in enumerate(args[:_check_index]):
        arg_type = annotations['args'][i]

        if isinstance(_v, arg_type):
            continue

        raise TypeError(
            "The type of '{}' does not match '{}' type".format(
                _v, arg_type.__name__
            )
        )
    print(*args)

In [8]:
type_checking_func(1, 2, '3', 'a', [1,2,3])


1 2 3 a [1, 2, 3]

In [9]:
type_checking_func(1, '2', '3', 'a')


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-9-ba8026bcdece> in <module>()
----> 1 type_checking_func(1, '2', '3', 'a')

<ipython-input-7-e1e639190327> in type_checking_func(*args)
     22         raise TypeError(
     23             "The type of '{}' does not match '{}' type".format(
---> 24                 _v, arg_type.__name__
     25             )
     26         )

TypeError: The type of '2' does not match 'int' type

In [10]:
type_checking_func(1, 2, '3', 'a')


1 2 3 a
  • 변수의 형 검사를 decorator로 만들어서 사용할 수도 있음

In [11]:
def check_argument_type(func):
    def wrapper(*args):
        annotations = func.__annotations__
        if (
            not isinstance(annotations, dict) or
            len(annotations) == 0
        ):
            return func(*args)

        try:
            check_index = annotations['args'].index(Ellipsis)
        except ValueError:
            check_index = len(annotations['args']) - 1

        for _i, _v in enumerate(args[:check_index]):
            _arg_type = annotations['args'][_i]

            if isinstance(_v, _arg_type):
                continue

            raise TypeError(
                "The type of '{}' does not match '{}' type".format(
                    _v, _arg_type.__name__
                )
            )
        return func(*args)
    return wrapper

In [12]:
@check_argument_type
def hello_func(*args: (int, int, ...)):
    print(*args)

In [13]:
hello_func(1, 2, '3', 'a')


1 2 3 a

In [14]:
hello_func(1, '2', '3', 'a')


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-14-34ded046e38f> in <module>()
----> 1 hello_func(1, '2', '3', 'a')

<ipython-input-11-31980d0dd80b> in wrapper(*args)
     21             raise TypeError(
     22                 "The type of '{}' does not match '{}' type".format(
---> 23                     _v, _arg_type.__name__
     24                 )
     25             )

TypeError: The type of '2' does not match 'int' type