단단한 파이썬 3. 타입 어노테이션

    들어가기 전

    이 글은 단단한 파이썬 3장을 공부하며 작성한 글입니다.

     


    타입 어노테이션이란? 

    • 타입 어노테이션은 변수, 반환 값의 타입을 알려줌.
    • 타입 어노테이션은 런타임 동안 파이썬에 아무런 영향을 주지 않음. (비용이 들지 않음) 
    • 타입 어노테이션은 개발자에게 의도를 전달하는 용도로 사용됨. 
    • 타입 어노테이션을 남발하는 것은 좋지 않음. (간결함을 잃음)
      • number: int = 0 같은 타입 어노테이션은 무의미함. 
    def find_workers_available_for_time(open_time: datetime.datetime) -> list[str]:
        workers = get_all_workers()
        available_workers = [worker for worker in workers
                             if is_available(worker)]
    
        if available_workers:
            return available_workers
    
        emergency_workers = [worker for worker in get_emergency_workers()
                             if is_available(worker)]
    
        if emergency_workers:
            return emergency_workers
    
        return [OWNER]

    이 코드에서 타입 어노테이션의 효용성은 다음과 같다. 

    • open_time (매개변수)가 어떤 타입인지를 알 수 있다. datetime인지 Unix Time인지는 이 함수를 쓰는 사람은 정확히 알 수 없는데, 명확해 짐. 
    • 함수 호출 결과로 여러 객체가 반환됨. 만약 반환 타입에 대한 어노테이션이 없었다면?
      • get_all_workers(), get_emergencey_workers(), [OWNER]가 각각 무슨 값을 반환하는지 한 Depth 더 조사해야함. 

     


    타입 어노테이션의 장점

    타입 어노테이션은 다음 두 가지 장점을 가진다. 

    • 자동완성 : 타입 어노테이션이 되어있다면, IDE에서 자동완성을 지원해 줌.
    • 타입 체커 : 타입 체커를 통해 코드베이스의 견고성을 올려줄 수 있음. 

     


    타입 체커를 이용한 무결성 확인 : 타입 어노테이션 장점

    "타입 체커"는 타입 어노테이션을 체크하고 타입 어노테이션이 잘못 사용된 것을 점검해주는 도구다. 타입 어노테이션과 타입 체커를 함께 이용하면 보다 단단한 코드를 작성할 수 있다. 

    # 3-2.py
    def read_file_and_reverse_it(filename: str) -> str:
        with open(filename) as f:
            # 바이트로 인코딩.
            return f.read().encode("utf-8")[::-1]

    위의 코드와 실행 결과를 살펴보면 타입 체커의 효과를 알 수 있다. 

    • 반환 타입은 str이어야 하는데 byte 타입을 반환함. 
      • 타입 어노테이션이기 때문에 런타임에는 아무런 영향을 미치지 않음.
      • 타입 체커를 실행해 미리 타입 체크를 하면 잘못 사용된 부분을 발견할 수 있음.
    $ mypy 3-2.py
    >>> 
    3-2.py:6: error: Incompatible return value type (got "bytes", expected "str")  [return-value]
    Found 1 error in 1 file (checked 1 source file)

    타입체커는 정확하게 'str 타입을 반환해야하는데 byte 타입을 반환했다'라는 것을 알려준다. 이 외에도 여러가지 예시들을 살펴볼 수 있다.

    def add_doubled_values(my_list: list[int]):
        my_list.update([x*2 for x in my_list])
    
    add_doubled_values([1, 2, 3])
    >>>
    3-3.py:3: error: "list[int]" has no attribute "update"  [attr-defined]
    Found 1 error in 1 file (checked 1 source file)

    List 타입에 특정 Attribute가 없는 타입 에러도 타입체커(mypy)를 이용해서 검출할 수 있다.

    ITALY = ['roma', 'pisa']
    SPAIN = ['madrid']
    
    
    def get_restaurant_name(city: str) -> str:
        if city in ITALY:
            return "Trattoria"
        if city in SPAIN:
            return "Hello restaurant"
        return None
    
    >>> 
    3-4.py:10: error: Incompatible return value type (got "None", expected "str")  [return-value]
    Found 1 error in 1 file (checked 1 source file)

    위 경우도 타입체커가 문제를 검출해낸다. String 타입을 기대했는데, None Type이 반환되는 것도 있기 때문에 타입체커가 문제가 있는 것을 알게된 것이다. 

     


    타입 어노테이션을 사용할 때

    타입체커는 정적 언어의 이점을 제공하지만, 여전히 파이썬이 동적 타입으로 동작할 수 있게 해준다. 그러나 타입 어노테이션을 사용하는 것 자체는 '새로운 비용'이기 때문에 필요한 경우에만 '잘' 사용하는 것이 중요하다. 그렇다면 타입 어노테이션과 타입 체커를 언제 사용하는 것이 좋을까?

    • 다른 곳에서 호출할 가능성이 높은 공개 API
    • 타입이 복잡한 곳, 비직관적인 곳을 강조하고 싶을 때
    • mypy가 타입이 필요하다고 하는 경우. 

    타입 힌트는 '옵션'이다. 따라서 잠깐만 쓰일 코드에는 사용하지 않고, 오랫동안 사용될 코드라면 타입 힌트를 사용하는 것이 좋다. 타입체커와 타입 어노테이션을 적절하게 이용하면 다음의 이점을 가질 수 있다.

    • 타입체커를 이용한 단단한 코딩
    • 타입 어노테이션을 이용한 가독성 확보. (내부 Attribute를 더 잘 알 수 있게 됨.

    댓글

    Designed by JB FACTORY