Programming/Python

[Python] 독스트링 (Docstrings)

Jonghyuk Baek 2021. 4. 25. 01:06

Introduction

파이썬은 동적 타입 언어(참고)라는 특성 때문에 코드의 문서화가 다른 언어에 비해 특히 더 중요하다.

 

하지만 코드를 개발하면서 따로 잘 정리된 문서를 작성하는 일은 만만치 않은 작업일 것이다. 또한 코드 설명 내용을 찾기 위해 주석을 일일이 뒤지거나 도큐멘테이션을 찾으러 다니는 것 또한 꽤 번거로운 일이다.

 

파이썬에서는 코드의 문서화와 편리한 문서 참조를 위해 기본 기능으로 독스트링(Docstring) 이라는 기능을 제공하는데, 오늘은 이것에 대해 알아볼 생각이다.

 


Docstrings

독스트링은 함수나 클래스, 모듈에 첨부할 수 있는, 큰 따옴표 세 개 (""") 혹은 작은 따옴표 세 개 (''')로 둘러싸여진 문자열이다. PEP 8 (Python Enhancement Proposals) 에서는 큰 따옴표 세 개를 권장하고 있다. 이 문자열에는 함수, 클래스, 모듈의 목적과 이를 이용할 때 필요한 세부 정보들을 담는 것이 관례이다. 이해를 위해 예시를 한번 보자.

Function docstring

# user_module.py
from math import sin, cos, pi


def projectile_landing_position(velocity, angle, g_constant):
    """
    원점에서 출발한 물체의 착륙 지점을 계산한다.

    XY 평면 (0,0) 좌표에서 X축 기준 angle 방향과
    velocity 값의 속력으로 출발한 물체가, -Y축 방향
    중력 가속도 g 하에 얼마의 x 값에서 y=0에 도달할 지 계산하는 함수.

    Args:
        velocity: 물체의 초기 속력 (0 < velocity)
        angle: 물체의 초기 이동 방향 (0 < angle < 180, degree)
        g_constant: 중력 가속도 (0 < g_constant)

    Raises:
        ValueError: 인자가 조건에 맞지 않는 경우

    Returns:
        landing_position: 물체의 y 좌표가 0에 도달했을 시점에서의 x 좌표
    """

    if velocity < 0:
        raise ValueError("velocity 는 0과 같거나 커야 합니다")
    if angle > 180 or angle < 0:
        raise ValueError("angle 은 [0~180] 범위의 값이어야 합니다")
    if g_constant <= 0:
        raise ValueError("g_constant 는 0보다 커야 합니다")

    velocity_x = velocity * cos(angle / 180 * pi)
    velocity_y = velocity * sin(angle / 180 * pi)

    terminal_time = velocity_y / g_constant
    landing_position = velocity_x * terminal_time

    return landing_position

이전 포스트의 샘플 코드에서 독스트링 부분을 조금 변형했다. 함수 레벨의 독스트링은 def 바로 다음 줄에서 시작하며 첫 줄에는 함수의 목적을, 다음 단락부터는 세부적인 함수의 동작에 대한 설명이나 인자, 반환 값 그리고 외부에서 처리되어야 하는 예외에 대해서 설명한다.

 

만약 인자나 반환 값, 혹은 발생시키는 예외가 없다면 해당 내용은 생략할 수 있다.

 

그리고 타입 힌트(참고)를 사용하고 있다면, 굳이 같은 내용을 독스트링에서 반복을 할 필요는 없다. 하지만 이런 타입 정보는 되도록 독스트링 혹은 타입 힌트 한 쪽에만 일관되게 작성하는 편이 좋다.

__doc__ attribute

이렇게 만들어 놓은 독스트링 문자열은 __doc__ 어트리뷰트를 통해 프로그램 실행 중에 직접 접근하는 것이 가능하다.

import user_module


print(user_module.projectile_landing_position.__doc__)

>>>
    원점에서 출발한 물체의 착륙 지점을 계산한다.

    XY 평면 (0,0) 좌표에서 X축 기준 angle 방향과
    velocity 값의 속력으로 출발한 물체가, -Y축 방향
    중력 가속도 g 하에 얼마의 x 값에서 y=0에 도달할 지 계산하는 함수.

    Args:
        velocity: 물체의 초기 속력 (0 < velocity)
        angle: 물체의 초기 이동 방향 (0 < angle < 180, degree)
        g_constant: 중력 가속도 (0 < g_constant)

    Raises:
        ValueError: 인자가 조건에 맞지 않는 경우

    Returns:
        landing_position: 물체의 y 좌표가 0에 도달했을 시점에서의 x 좌표

코드 실행 중 대상의 독스트링 문자열을 가져오는 것이 가능하다!

 

이런 기능은 IPython 노트북과 같은 대화형 개발 도구를 사용할 때 특히나 유용하다. 따로 웹 문서에서 도큐멘테이션 페이지를 들어갈 필요 없이 print(__doc__) 커맨드를 통해 필요한 정보를 얻을 수 있다. 독스트링 작성이 잘 되어있는 라이브러리일수록 유용하다.

Class docstring

클래스 단위에서도 독스트링을 따로 작성할 수 있다. 클래스 또한 첫 줄에는 클래스의 목적을 설명하며, 이후 단락들에는 클래스의 세부 동작에 대해서 설명하면 된다. 

 

해당 클래스를 상속받아 기존의 보호 어트리뷰트나 메서드와 상호작용하는 다른 클래스가 있다면, 이런 세부 내용에 대해서도 설명해 주어야 한다. 아래는 간단한 예시이다.

class Projectile:
    """
    발사체의 속성을 표현한다.
    
    Projectile 을 상속받는 클래스는 'dynamics' 메서드를 오버라이드해
    물체의 세부 dynamics 를 정의해야 한다.

    Attributes:
        mass: 물체의 질량 (float)
        ...

    Methods:
        info: 물체의 속성을 출력
        dynamics: 물체의 세부 dynamics 로, 하위 클래스에서 내용을 정의
        ...
    """

첫 줄에는 클래스 기능을 요약하고, 필요하다면 추가 단락을 통해 세부 내용을 설명한다. 클래스 어트리뷰트멤버 메소드에 대한 간략한 소개도 넣어야 한다. 이 또한 같은 방식으로 __doc__ 어트리뷰트를 통해 접근이 가능하다.

 

멤버 함수 각각에 대해서는 위의 function docstring에서 설명한 방식대로 독스트링을 따로 작성해 주면 된다.

Module docstring

마지막은 모듈 단위의 독스트링이다. 모듈 독스트링은 코드 첫 줄에 위치하며 모듈의 목적과 기능을 설명해야 한다. 작성 방식은 이전 예시들과 동일하다. 다만 이번에는 내부에 정의된 클래스와 함수들에 대한 간략한 설명을 넣어 주어야 한다. 아래는 모듈 단위의 독스트링 예시이다.

# user_module.py
"""
발사체 궤적 계산을 위한 라이브러리.

이 모듈은 특정 조건 하에서의 발사체 움직임을 간단화된 방식으로 계산하는 것을 도와준다.

Classes:
    Projectile: 발사체의 속성을 표현한다
    ...

Functions:
    projectile_landing_position(velocity, angle, g_constant): 원점에서 출발한 물체의 착륙 지점을 계산한다
    ...

"""

Python documentation generator

문서화 표준을 따라 독스트링을 작성해 놓았다면, 코드 곳곳에 적혀있는 독스트링을 모아 정형화된 문서 형태로 바꾸어 주는 도구를 이용할 수 있다. 예를 들면, 파이썬 내장 도구인 pydoc을 이용하면 사용자 정의 모듈을 포함한, 인터프리터에서 찾을 수 있는 모든 독스트링 문서를 정리해 주는 로컬 웹 서버를 실행할 수 있다. 예시를 한번 보자.

pydoc

$ python -m pydoc -p 3333

>>
Server ready at http://localhost:3333/
Server commands: [b]rowser, [q]uit
server>

이런 식으로 웹 서버를 켜고 링크를 따라 들어가 보면, (b를 눌러도 된다)

 

인터프리터에서 찾을 수 있는 독스트링 문서를 전부 정리해 준다.

 

위와 같이 인터프리터 단에서 찾을 수 있는 모든 문서를 정리해 보여준다! 아까 만들어 놓은 user_module.py 의 문서를 찾아 링크를 타고 가 보자.

 

모듈, 클래스, 함수 독스트링이 모두 정리되어 보여진다.

 

작성한 독스트링들을 잘 정리해 보여주며, 문서화 컨벤션에 맞추어 작성했을 경우 알아서 문서 링크까지 걸어준다. 전체적인 독스트링 작성 컨벤션에 대해서는 PEP 257 를 참고하자. 스핑크스(Sphinx) 같은 도구를 이용하면 HTML이나 Markdown 등 여러 형식의 파일을 만들어 주는 등, 다양한 방향으로 독스트링 문서를 이용할 수 있으니 한번 알아보고 응용해 보자.


아래는 참고할 만한 사이트들이다.

References

PEP 257 -- Docstring Conventions

https://www.python.org/dev/peps/pep-0257/

pydoc Documentation

https://docs.python.org/3/library/pydoc.html

Sphnix Documentation

https://www.sphinx-doc.org/en/master/

[Python/VSCode] 독스트링 스타일과 autoDocstring

https://jh-bk.tistory.com/36

 

아래는 예시 작성에 쓰인 모듈 코드 전문이다. 적혀있는 내용을 구현해 놓지는 않아서 독스트링 작성 방식에 대해 참고만 하면 좋겠다.

# user_module.py
"""
발사체 궤적 계산을 위한 라이브러리.

이 모듈은 특정 조건 하에서의 발사체 움직임을 간단화된 방식으로 계산하는 것을 도와준다.

Classes:
    Projectile: 발사체의 속성을 표현한다
    ...

Functions:
    projectile_landing_position(velocity, angle, g_constant): 원점에서 출발한 물체의 착륙 지점을 계산한다
    ...

"""

from math import sin, cos, pi


class Projectile:
    """
    발사체의 속성을 표현한다.

    Projectile 을 상속받는 클래스는 'dynamics' 메서드를 오버라이드해
    물체의 세부 dynamics 를 정의해야 한다.

    Attributes:
        mass: 물체의 질량 (float)
        ...

    Methods:
        info: 물체의 속성을 출력
        dynamics: 물체의 세부 dynamics 로, 하위 클래스에서 내용을 정의
        ...
    """


def projectile_landing_position(velocity, angle, g_constant):
    """
    원점에서 출발한 물체의 착륙 지점을 계산한다.

    XY 평면 (0,0) 좌표에서 X축 기준 angle 방향과
    velocity 값의 속력으로 출발한 물체가, -Y축 방향
    중력 가속도 g 하에 얼마의 x 값에서 y=0에 도달할 지 계산하는 함수.

    Args:
        velocity: 물체의 초기 속력 (0 < velocity)
        angle: 물체의 초기 이동 방향 (0 < angle < 180, degree)
        g_constant: 중력 가속도 (0 < g_constant)

    Raises:
        ValueError: 인자가 조건에 맞지 않는 경우

    Returns:
        landing_position: 물체의 y 좌표가 0에 도달했을 시점에서의 x 좌표
    """

    if velocity < 0:
        raise ValueError("velocity 는 0과 같거나 커야 합니다")
    if angle > 180 or angle < 0:
        raise ValueError("angle 은 [0~180] 범위의 값이어야 합니다")
    if g_constant <= 0:
        raise ValueError("g_constant 는 0보다 커야 합니다")

    velocity_x = velocity * cos(angle / 180 * pi)
    velocity_y = velocity * sin(angle / 180 * pi)

    terminal_time = velocity_y / g_constant
    landing_position = velocity_x * terminal_time

    return landing_position
반응형