1 함수 Docstring

함수는 결국 입력값이 들어가서 블랙박스 작업을 통해 결과가 나타나는 구조를 갖는다. 따라서, 함수가 하는 기능이 무엇이고, 어떤 입력값을 갖는지, 결과값은 어떤 것이고, 사용례는 무엇인지 문서화시킬 필요가 있다. 문제는 파이썬의 경우 몇가지 Docstring 이 존재한다는 것이다. 이것은 함수의 본질적인 기능과는 아무런 관련이 없지만, 향후 유지보수나 기능개선, 사용성 등 다양한 영향을 미치게 된다. 대표적으로 구글 스타일과 numpy 스타일을 살펴보자.1

구글 문서 스타일도 많이 사용되고 있지만,
numpy scipy pandas sklearn matplotlib dask 등 데이터 과학에서 많이 사용되는 팩키지에 numpy 스타일 가이드가 적용되고 있다.

구글 스타일

def func(arg1, arg2):
    """Summary line.

    Extended description of function.

    Args:
        arg1 (int): Description of arg1
        arg2 (str): Description of arg2

    Returns:
        bool: Description of return value

    """
    return True

numpy 스타일

def func(arg1, arg2):
    """Summary line.

    Extended description of function.

    Parameters
    ----------
    arg1 : int
        Description of arg1
    arg2 : str
        Description of arg2

    Returns
    -------
    bool
        Description of return value

    """
    return True

2 numpy 스타일 사례

대표적으로 pandas의 코딩 스타일을 살펴보면 numpy 스타일을 차용하여 작성된 것을 확인할 수 있다.

import pandas as pd

help(pd.DataFrame.agg)
Help on function aggregate in module pandas.core.frame:

aggregate(self, func, axis=0, *args, **kwargs)
    Aggregate using one or more operations over the specified axis.
    
    .. versionadded:: 0.20.0
    
    Parameters
    ----------
    func : function, str, list or dict
        Function to use for aggregating the data. If a function, must either
        work when passed a DataFrame or when passed to DataFrame.apply.
    
        Accepted combinations are:
    
        - function
        - string function name
        - list of functions and/or function names, e.g. ``[np.sum, 'mean']``
        - dict of axis labels -> functions, function names or list of such.
    axis : {0 or 'index', 1 or 'columns'}, default 0
            If 0 or 'index': apply function to each column.
            If 1 or 'columns': apply function to each row.
    *args
        Positional arguments to pass to `func`.
    **kwargs
        Keyword arguments to pass to `func`.
    
    Returns
    -------
    scalar, Series or DataFrame
    
        The return can be:
    
        * scalar : when Series.agg is called with single function
        * Series : when DataFrame.agg is called with a single function
        * DataFrame : when DataFrame.agg is called with several functions
    
        Return scalar, Series or DataFrame.
    
    The aggregation operations are always performed over an axis, either the
    index (default) or the column axis. This behavior is different from
    `numpy` aggregation functions (`mean`, `median`, `prod`, `sum`, `std`,
    `var`), where the default is to compute the aggregation of the flattened
    array, e.g., ``numpy.mean(arr_2d)`` as opposed to
    ``numpy.mean(arr_2d, axis=0)``.
    
    `agg` is an alias for `aggregate`. Use the alias.
    
    See Also
    --------
    DataFrame.apply : Perform any type of operations.
    DataFrame.transform : Perform transformation type operations.
    core.groupby.GroupBy : Perform operations over groups.
    core.resample.Resampler : Perform operations over resampled bins.
    core.window.Rolling : Perform operations over rolling window.
    core.window.Expanding : Perform operations over expanding window.
    core.window.EWM : Perform operation over exponential weighted
        window.
    
    Notes
    -----
    `agg` is an alias for `aggregate`. Use the alias.
    
    A passed user-defined-function will be passed a Series for evaluation.
    
    Examples
    --------
    >>> df = pd.DataFrame([[1, 2, 3],
    ...                    [4, 5, 6],
    ...                    [7, 8, 9],
    ...                    [np.nan, np.nan, np.nan]],
    ...                   columns=['A', 'B', 'C'])
    
    Aggregate these functions over the rows.
    
    >>> df.agg(['sum', 'min'])
            A     B     C
    sum  12.0  15.0  18.0
    min   1.0   2.0   3.0
    
    Different aggregations per column.
    
    >>> df.agg({'A' : ['sum', 'min'], 'B' : ['min', 'max']})
            A    B
    max   NaN  8.0
    min   1.0  2.0
    sum  12.0  NaN
    
    Aggregate over the columns.
    
    >>> df.agg("mean", axis="columns")
    0    2.0
    1    5.0
    2    8.0
    3    NaN
    dtype: float64

3 numpy 스타일 적용방법

pyment 팩키지를 사용해서 간단히 다양한 스타일을 적용시킬 수 있다.

  • numpy → numpydoc
  • 구글 → google
  • reStructured-text → reST
  • epytext → javadoc

문서 스타일 적용전

cat code/bmi_function.py
def calc_bmi(height = 170.5, weight=70.9):
    """ Calculate BMI based on meter scale 

    Parameters
    ----------
    height : float
         (Default value = 170.5)
    weight : float
         (Default value = 70.9)

    Returns
    -------
    bmi: float
        BMI value
    """
    
    height = height * 0.01 
    weight = weight
    bmi = weight / height ** 2
    # print("BMI:", bmi)
    return(bmi)

numpy 스타일 적용후

pyment -w -o numpydoc code/bmi_numpy_function.py
cat code/bmi_numpy_function.py

def calc_bmi(height = 170.5, weight=70.9):
    """

    Parameters
    ----------
    height :
        (Default value = 170.5)
    weight :
        (Default value = 70.9)

    Returns
    -------

    
    """
    height = height * 0.01 
    weight = weight
    bmi = weight / height ** 2
    print("BMI:", bmi)
    return(bmi)

마지막 터치를 주어 함수에 대한 문서 작업을 완료한다.

def calc_bmi(height = 170.5, weight=70.9):
    """ Calculate BMI based on meter scale 

    Parameters
    ----------
    height : float
         (Default value = 170.5)
    weight : float
         (Default value = 70.9)

    Returns
    -------
    bmi: float
        BMI value
    """
    
    height = height * 0.01 
    weight = weight
    bmi = weight / height ** 2
    print("BMI:", bmi)
    return(bmi)

4 (하위) 팩키지, 모듈

팩키지, 하위 팩키지, 모듈에 대한 문서화를 진행할 경우 다음과 같이 진행한다.

cat kbmi/kbmi/__init__.py
"""
Home of Korean BMI  
============================
kbmi is a complete package for calculating BMI in Korea.
"""

만약 하위 팩키지와 모듈이 있다면 """ …. """ 사이 docstring을 작성하면 된다.

파이썬 팩키지의 구조는 다음과 같이 팩키지, 하위 팩키지, 모듈로 구성된다.

kbmi/               <------ 팩키지
|-- __init__.py
|-- input           <------- 하위 팩키지 
| |-- __init__.py
| |-- clean_input.py
| |-- transform.py  <------- 모듈
|-- calculate
| |-- __init__.py
| |-- get_bmi.py 
|-- utils.py
 

데이터 과학자 이광춘 저작

kwangchun.lee.7@gmail.com