2013년 10월 1일 화요일

Python 함수(function)

함수(function)
여기에서는 당신이 최소한 다른 하나의 computer language의 함수(function) 정의와 argument, parameter 등의 개념에 익숙하다고 가정한다.

기본 함수(function) 정의
Python 함수(function) 정의의 기본 문법은 다음과 같다.

def name(parameter1, parameter2, ...):
    body

제어 구조(control structure) 처리 과정에서 Python은 함수(function) 정의의 body를 구분하는데 들여쓰기(indentation)을 사용한다. 다음 예제는 함수(function) body factorial code를 넣었다. 이 함수(function) factorial 값을 취득하기 위해서 사용되며 fact라고 이름을 정했다.

>>> def fact(n):
...     """주어진 숫자의 factorial 값을 반환한다."""
...     r = 1
...     while n > 0:
...         r = r * n
...         n = n - 1
...     return r
...

두 번째 line option으로 documentation string 또는 docstring으로 취급된다. 여기에 기술된 내용은 fact.__doc__을 사용하여 출력할 수 있다. docstring의 목적은 함수(function)에서 사용하는 parameter들과 외면의 동작에 대한 설명에 있으며, 주석은 code가 내부에서 어떻게 동작하는지에 대한 정보를 기술하는데 있다. docstring은 함수(function) 정의하는 첫 line 다음에 바로 작성되는 문자열이며 일반적으로 삼중 따옴표(quotes) 사용하여 복수의 line을 사용한 설명을 허용한다. document string들의 첫 line을 발췌하여 볼 수 있는 browsing tool들도 사용할 수 있다. 복수의 line을 사용하는 documentation string에서 첫 line에 함수(function)의 개요를 제공하고 두 번째 line은 공백으로 두며 다음에 나머지 정보를 기술하는 것이 standard practice이다. 마지막 line return 후에 표시된 값을 함수(function) 호출한 code로 반환한다.

procedure 또는 function?
몇몇 language들에서는 값을 반환하지 않는 함수(function) procedure라고 부른다. return 구문이 없는 함수(function)을 작성할 수 있지만 이는 실질적으로 procedure는 아니다. 모든 Python procedure는 함수(function)이다. procedure body 내에 return이 없다면 특수 Python value None이 반환되며 return arg가 수행되면 arg의 값이 즉시 반환된다. return이 수행되고 나면 함수(function) body 내의 무엇도 수행되지 않는다. Python은 진정한 procedure를 가지고 있지 않으므로 이 두 가지 type들은 function으로 참조할 것이다.

모든 Python 함수(function)들은 값들을 반환하지만 이 함수(function)의 반환 값의 사용은 전적으로 programmer의 몫이다.

>>> fact(4)
24
>>> x = fact(4)
>>> x
24
>>>

첫 번째 line에서는 반환값을 변수에 할당하지 않았다. fact 함수(function)의 값은 interpreter에서만 출력되는 것을 볼 수 있다. 세 번째 line에서는 반환값을 변수 x에 할당하였으며 이를 이용할 수 있다.

함수(function) parameter option
대부분의 함수(function)들은 parameter들이 요구되며 각각의 language는 함수(function) parameter들을 정의하는 것에 대한 독자적인 사양을 가지고 있다. Python은 유연하게 함수(function) parameter들의 정의에 세 가지 option들을 제공한다. 여기에서는 이들을 설명할 것이다.

위치적인(positional) parameter
Python에서 함수(function) parameter들을 전달하는 가장 단순한 방식은 위치(position)이다. 함수(function)의 첫 번째 line에 각 parameter변수명으로 정의하도록 지정할 수 있다. 함수(function)이 호출 시에 호출 code 내에서 사용된 parameter들은 함수(function) parameter 변수들에 순서대로 대입된다. 다음 함수(function) x y 만큼 제곱하는 것을 연산한다.

>>> def power(x, y):
...     r = 1
...     while y > 0:
...         r = r * x
...         y = y - 1
...     return r
...
>>> power(3, 3)
27

method는 호출 code에서 사용된 parameter의 숫자가 정의된 함수(function) 내의 parameter의 숫자와 정확하게 일치해야 한다. 일치하지 않는다면 TypeError exception이 발생될 것이다.

>>> power(3)
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
TypeError: power() takes exactly 2 positional arguments (1 given)
>>>

Default value
함수(function) parameter들은 함수(function) 선언 시에 기본값(default value)을 다음과 같이 함수(function) 정의의 첫 line 내에서 할당하여 가지게 할 수 있다.
def fun(arg1, arg2=default2, arg3=default3, . . .)
어떤 숫자의 parameter들이라도 기본값(default value)들을 줄 수 있다. 기본값을 가진 parameter들은 반드시 parameter list 내에서 마지막 parameter들로 정의되어야 한다. 이는 다른 대부분의 language들처럼 Python이 위치(position) 기반으로 argument들과 parameter들이 쌍을 짓기 때문이다. 함수(function)에는 충분한 argument들이 있어야 한다. 함수(function) parameter list 내에서 기본값(default value) 가지지 않은 마지막 parameter argument를 가져야 하기 때문이다. 더욱 유연한 mechanism은 이 다음에 오는 “parameter 이름으로 argument들을 전달에서 알아볼 것이다.

다음 함수(function) x y 만큼 제곱하는 것을 연산한다. 하지만 함수(function) 호출에서 y 값이 주어지지 않는다면 기본값인 2를 사용하여 2 제곱을 하는 기능을 제공한다.

>>> def power(x, y=2):
...     r = 1
...     while y > 0:
...         r = r * x
...         y = y - 1
...     return r
...

다음의 대화형 session에서 기본 argument의 효과를 볼 수 있을 것이다.

>>> power(3, 3)
27
>>> power(3)
9

parameter 이름으로 argument들을 전달
함수(function)에 위치가 아니라 parameter의 이름을 사용하여 argument들을 전달할 수도 있다. 이전의 대화형 예제에서 다음과 같이 시도할 수 있다.

>>> power(2, 3)
8
>>> power(3, 2)
9
>>> power(y=2, x=3)
9

마지막에 이름으로 argument들을 받아서 수행된 power를 볼 때 argument들의 순서는 상관이 없다는 것을 알 수 있다. argument들은 power 정의 내에 동일한 이름의 parameter에 대입되어 3^2 계산값을 출력한다. 이런 type argument 전달은 keyword passing이라고 한다.
keyword passing Python 함수(function)의 기본 argument 기능과 결합하여 사용하면 매우 유용하다. 특히 기본값을 가지는 argument들의 수가 많은 함수(function)을 정의할 때 유용하게 사용될 수 있다. 예를 들어 현재 directory 내의 file들에 대한 정보를 가진 list를 생성하며 결과값에 file 크기, 마지막 수정일 등과 같은 정보를 포함할 지를 Boolean argument들을 사용하여 제어할 수 있도록 함수(function) 작성하려고 한다. 이 함수(function)은 다음과 같이 정의할 수 있다.

def list_file_info(size=False, create_date=False, mod_date=False, ...):
    ...get file names...
    if size:
        # code to get file sizes goes here
        if create_date:
            # code to get create dates goes here
    .
    .
    .
    return fileinfostructure

이 후에 다른 code에서 keyword argument를 전달하여 특정 정보만 나타내도록 함수(function)을 호출할 수 있다. 다음 예제에서는 file 크기, 마지막 수정일을 표시하고 생성일은 표시하지 않도록 함수(function) 호출하고 있다.

fileinfo = list_file_info(size=True, mod_date=True)

이런 type argument handling은 특히 매우 복잡한 동작을 하는 함수(function) GUI에서 이와 같은 함수(function)가 사용되는 곳에 적합하다. 만약 당신이 Python에서 GUI를 만드는데 Tkinter package를 사용한다면 이와 같은 option keyword 이름으로 된 argument들의 사용이 매우 유용하다는 것을 깨닫게 될 것이다.

argument들의 변수(variable) 숫자들
Python 함수(function) argument들의 변수(variable) 숫자를 다룰 수 있게 정의할 수도 있다. 이는 두 가지 다른 방식으로 수행할 수 있다. 하나의 방식은 상대적으로 친숙한 case로 해당 argument list의 끝에 있는 정해지지 않은 숫자의 argument들을 하나의 list로 수집하는 것을 처리한다. 다른 방식은 함수(function) parameter list 내에 해당되는 이름의 parameter를 가지고 있지 않은 임의 숫자의 keyword 전달 argument들을 dictionary로 수집할 수 있다. 이 두 가지 mechanism들을 다음에 논의할 것이다.

정해지지 않은 숫자의 위치 기반 argument들의 처리
함수(function)의 마지막 parameter name접두어로 *을 사용하면 함수(function) 호출에서 모든 keyword를 가지지 않은 초과 argument들을 (, 다른 parameter에 할당되지 않은 위치(position) 기반의 argument) 수집하여 주어진 parameter tuple로 할당한다. 다음은 숫자들의 list 내에서 최대값을 찾는 함수(function)을 단순한 방식으로 구현한 것이다.
첫 번째로 함수(function) 구현한다.

>>> def maximum(*numbers):
...     if len(numbers) == 0:
...         return None
...     else:
...         maxnum = numbers[0]
...         for n in numbers[1:]:
...             if n > maxnum:
...                 maxnum = n
...         return maxnum
...

다음은 이 함수(function)의 동작을 test 해보자.

>>> maximum(3, 2, 8)
8
>>> maximum(1, 5, 9, -2, 2)
9
>>>

정해지지 않은 숫자의 keyword로 전달되는 argument들의 처리
임의의 숫자를 가진 keyword argument들도 처리할 수 있다. 만약 parameter list 내에 마지막 parameter접두어로 **을 사용하면 모든 keyword로 전달되는 초과 argument들을 dictionary로 수집할 것이다. dictionary 내 각 항목의 index는 초과 argument keyword(parameter 이름) 이 될 것이다. 항목의 값은 해당 argument 그 자체가 된다. keyword로 전달된 argument는 전달된 keyword가 해당 함수(function) parameter 이름들과 일치하는 것이 없다면 이 context를 초과하게 된다.
다음은 이를 설명하는 예제이다.

>>> def example_fun(x, y, **other):
...     print("x: {0}, y: {1}, keys in 'other': {2}".format(x,
...             y, list(other.keys())))
...     other_total = 0
...     for k in other.keys():
...         other_total = other_total + other[k]
...     print("The total of values in 'other' is {0}".format(other_total))

대화형 session에서 이 함수(function)을 시험해보면 foo bar keyword들로 전달된 argument들을 처리되는 것을 볼 수 있으며 함수(function) 정의에서 parameter 이름들이 없어도 처리할 수 있다는 것을 알 수 있을 것이다.

>>> example_fun(2, y="1", foo=3, bar=4)
x: 2, y: 1, keys in 'other': ['foo', 'bar']
The total of values in 'other' is 7

argument 전달 기술들의 혼합
Python 함수(function)들의 모든 argument 전달 기능들을 동시에 사용할 수도 있다. 하지만 주의를 기울이지 않으면 혼란을 줄 수 있다. 당신의 행동은 rule에 의해서 통제된다. 상세 내용은 공식 documentation(http://docs.python.org/3/tutorial/controlflow.html#keyword-arguments)을 참조하라.

변경 가능한 (mutable) object argument
argument들은 object 참조(reference) 전달된다. parameter object에 대한 신규 참조(reference)가 된다. parameter tuple, string, number와 같은 수정 불가 (immutable) object들을 사용해도 해당 함수(function) 외부에서는 영향을 받지 않는다. 하지만 list, dictionary, class instance 같은 수정 가능한 (mutable) object를 전달하여 변경을 한 경우에는 해당 함수(function) 외부에서 argument로 참조하는 해당 object 자체에 변경 사항이 적용된다. 해당 parameter 재할당은 argument 자체에 영향을 미치지 않는다. 다음 예제를 통해 이를 확인할 수 있다.

>>> def f(n, list1, list2):
...     list1.append(3)
...     list2 = [4, 5, 6]
...     n = n + 1
...
>>> x = 5
>>> y = [1, 2]
>>> z = [4, 5]
>>> f(x, y, z)
>>> x, y, z
(5, [1, 2, 3], [4, 5])

함수(function) f()가 시작될 때는 초기 변수들과 함수(function) parameter들은 동일한 object들을 참조한다.
함수(function) f()의 마지막에서는 n list2는 다른 object들을 참조하는 반면에 y에는 해당 함수(function) 내에서 list1을 변경한 내용이 반영되었다.
위의 도식도는 함수(function) f가 호출되면 어떤 과정으로 동작하는 지를 설명하고 있다. 변수 x는 수정 불가 (immutable) object이므로 변경되지 않는다. 대신 함수(function) parameter n은 새로운 값 6을 참조하도록 지정된다. 이와 유사하게 변수 z도 변경되지 않고 함수(function) f 내부에서 해당 parameter list2가 새로운 object [4, 5, 6]를 참조하도록 지정된다. y만 변경되었음을 볼 수 있는데 이 변수가 가리키는 실제 list가 변경되었기 때문이다.

지역(local), 비지역(nonlocal), 전역(global) 변수(variable)
이 글의 첫 부분의 함수(function) fact 정의를 살펴보자.

def fact(n):
    """주어진 숫자의 factorial 값을 반환한다."""
    r = 1
    while n > 0:
        r = r * n
        n = n - 1
    return r

변수 r n은 모두 factorial 함수(function) 호술 부분에서만 사용되는 지역 변수 (local variable)이다. 해당 함수(function)이 실행되는 동안에 이들 변수의 변경 사항들은 함수(function) 외부의 변수들에는 영향을 미치지 않는다. 함수(function) parameter list 내의 변수들과 fact r = 1과 같이 함수(function) 내에서 생성되어 할당되는 변수들은 함수(function)에 대해서 지역적(local)인 특징을 가진다.
변수가 사용되기 전에 global 구문을 사용하여 변수(variable) 선언하여 명확하게 전역 변수 (global variable) 만들 수 있다. 전역 변수 (global variable)들은 특정 함수(function)에서 접근과 변경이 가능하다. global 구문으로 선언되거나 함수(function) 외부에서 code로 선언된 전역 변수 (global variable)들은 특정 함수(function)을 벗어나서 다른 함수(function)들에서 접근과 변경도 가능하다. 다음 예제를 통해 지역 변수 (local variable)와 전역 변수 (global variable)의 차이점을 알아볼 것이다.

>>> def fun():
...     global a
...     a = 1
...     b = 2
...

위에서는 전역 변수(global variable) a와 지역 변수 (local variable) b를 취급하는 함수(function)을 정의하였다.
다음으로는 a b에 변경을 시도하여 test를 수행할 것이다.

>>> a = "one"
>>> b = "two"
>>> fun()
>>> a
1
>>> b
'two'

fun 내에서 a에 대한 할당은 fun 외부에 존재하는 전역 변수 (global variable) a에 대한 할당이다. fun 내에서 a global 구문을 통해 전역 변수로 지정되므로 값이 “one”에서 1로 할당되어 변경된다. b는 이와 동일하게 진행되지 않는다. fun 내부의 b는 지역 변수 (local variable)이며 처음에는 fun 외부의 변수 b와 동일한 값을 참조하지만 b에 대한 할당은 fun 함수(function)에 한정된 (local) 신규 값을 가리키도록 한다.
global 구문과 유사하게 nonlocal 구문이 있다. 이는 해당 scope를 감싸고 있는 scope에서 직전에 bind된 변수를 참조하는 식별자를 사용한다. 앞으로 scope namespace에 대해서 더욱 상세하게 다룰 것이다. global에는 최상위 변수(variable)가 사용되며, nonlocal은 해당 scope를 감싸고 있는 scope에서 특정 변수(variable)을 참조할 수 있다. 다음 code를 통해 이를 살펴볼 수 있다.

File nonlocal.py
g_var = 0 # inner_test 내의 g_var는 최상위 g_var bind 된다.
nl_var = 0

print("top level -> g_var: {0} nl_var: {1}".format(g_var, nl_var))

def test():
    nl_var = 2 # inner_test 내의 nl_var test 내의 nl_var bind 된다.

    print("in test -> g_var: {0} nl_var: {1}".format(g_var, nl_var))

    def inner_test():
        global g_var # inner_test 내의 g_var는 최상위 g_var bind 된다.
        nonlocal nl_var # inner_test 내의 nl_var test 내의 nl_var bind 된다.

        g_var = 1
        nl_var = 4

        print("in inner_test -> g_var: {0} nl_var: {1}".format(g_var, nl_var))

    inner_test()
    print("top level -> g_var: {0} nl_var: {1}".format(g_var, nl_var))

이를 수행하면 code는 다음과 같은 결과를 출력한다.

top level-> g_var: 0 nl_var: 0
in test-> g_var: 0 nl_var: 2
in inner_test-> g_var: 1 nl_var: 4
in test-> g_var: 1 nl_var: 4
top level-> g_var: 1 nl_var: 0

최상위 nl_var의 값은 inner_test global nl_var line을 포함하지 않는 한 영향을 받지 않을 것이다.
마지막 line에서 함수(function) 외부에 존재하는 변수에 할당을 하려고 한다면 해당 변수를 명확하게 nonlocal 이나 global으로 선언해야 한다는 것을 알 수 있다. 하지만 함수(function) 외부에 존재하는 변수에 접근하려고 한다면 nonlocal이나 global으로 선언할 필요는 없다. 만약 Python local 함수(function) scope 내에서 변수(variable) 이름을 찾지 못한다면 global scope 내에서 변수(variable) 이름을 찾으려고 할 것이다. 이런 이유로 전역 변수(global variable)들에 대한 접근은 자동적으로 정확하게 전역 변수(global variable)에 전달될 것이다. 사견으로는 이런 shortcut을 사용하는 것을 권장하지 않는다. 모든 전역 변수(global variable)들이 명확하게 global로 선언되는 것이 읽는 이에게 더욱 명확하다. 더 나아가서는 드문 경우에만 함수(function)들 내에서 전역 변수(global variable)들을 사용하도록 제한하려고 할 수 있다.

함수(function) 변수(variable)에 할당
함수(function)은 다음 예제에서 볼 수 있듯이 다른 Python object들처럼 변수(variable)에 할당될 수 있다.

>>> def f_to_kelvin(degrees_f): # 함수(function)을 정의
...     return 273.15 + (degrees_f - 32) * 5 / 9
...
>>> def c_to_kelvin(degrees_c): # 함수(function)을 정의
...     return 273.15 + degrees_c
...
>>> abs_temperature = f_to_kelvin # 함수(function)을 변수(variable)에 할당
>>> abs_temperature(32)
273.14999999999998
>>> abs_temperature = c_to_kelvin # 함수(function)을 변수(variable)에 할당
>>> abs_temperature(0)
273.14999999999998

이들을 list, tuple, dictionary에 포함시킬 수 있다.

>>> t = {'FtoK': f_to_kelvin, 'CtoK': c_to_kelvin}
>>> t['FtoK'](32) # dictionary 내의 값으로 함수(function)에 접근.
273.14999999999998
>>> t['CtoK'](0) # dictionary 내의 값으로 함수(function)에 접근.
273.14999999999998

함수(function)을 참조하는 변수(variable)는 정확하게 해당 함수(function)와 동일한 방식을 사용할 수 있다. 위의 예제는 dictionary를 사용하여 다른 함수(function)들을 key로 사용된 문자열의 값으로 호출할 수 있다는 것을 보여준다. 이는 다른 함수(function)들을 string 값을 기준으로 선택해야 하는 상황에서 일반적인 pattern이며 C Java같은 language들에서는 switch 구조로 구현된다.

lambda 표현식(expression)
위에서 보았던 짧은 함수(function) lambda 표현식(expression)의 형태로 정의할 수도 있다.

lambda parameter1, parameter2, . . .: expression

lambda 표현식은 inline으로 즉시 정의할 수 있는 작은 익명 함수(function)이다. list의 정렬 방식으로 사용되는 핵심 함수(function)같이 작은 함수(function)을 다른 함수(function)으로 전달해야 할 때가 종종 발생한다. 이와 같은 경우에 일반적으로 큰 함수(function)는 필요하지 않지만 사용되는 곳마다 함수(function) 따로따로 정의하는 것도 곤란한 일이다. 이전 예제에서 dictionary 부분에 함수 정의까지 할 수 있다.

>>> t2 = {'FtoK': lambda deg_f: 273.15 + (deg_f - 32) * 5 / 9,
...             'CtoK': lambda deg_c: 273.15 + deg_c}
>>> t2['FtoK'](32)
273.14999999999998

위의 예제는 dictionary value lambda 표현식을 사용하여 함수(function)을 정의하였다. lambda 표현식의 value가 자동으로 반환되므로 lambda 표현식에서는 return 구문을 가지지 못한다는 것을 주의하라.

생성자(generator) 함수(function)
생성자(generator) 함수(function)은 자신만의 반복자(iterator) 정의하는데 사용할 수 있는 특수한 종류의 함수(function)이다. 생성자(generator) 함수(function) 정의하면 yield keyword를 사용하여 반복(iteration)의 값을 각각 반환한다. 더 이상의 반복(iteration)을 하지 않는다면 빈 return 구문이나 해당 함수(function) 마지막 부분의 흐름 차단이 반복(iteration)을 종료한다. 일반적인 함수(function)와는 달리 생성자(generator) 함수(function) 내의 지역 변수(local variable)는 호출될 때부터 다음 호출까지 저장된다.

>>> def four():
...     x = 0 # x의 초기값을 0으로 지정.
...     while x < 4:
...         print("in generator, x =", x)
...         yield x # x현재값을 반환.
...         x += 1 # x의 값을 증가 시킨다.
...
>>> for i in four():
...     print(i)
...
in generator, x = 0
0
in generator, x = 1
1
in generator, x = 2
2
in generator, x = 3
3

생성자(generator) 함수(function)생성자(generator)가 수행될 횟수로 제한된 while loop를 가지고 있다. 어떻게 사용되느냐에 따라 종료 조건을 가지지 않은 생성자(generator)는 호출 시에 무한 loop를 발생시킬 수 있다.
또한 생성자(generator) 함수(function) in을 사용하여 생성자(generator)가 생성하는 순서열 내에 해당 값이 포함되는 지를 알아볼 수도 있다.

>>> 2 in four()
in generator, x = 0
in generator, x = 1
in generator, x = 2
True
>>> 5 in four()
in generator, x = 0
in generator, x = 1
in generator, x = 2
in generator, x = 3
False

장식자(decorator)
함수(function) Python에서 최고 수준의 object이므로 위에서 본 것처럼 변수(variable)에 할당될 수 있다. 함수(function)은 다른 함수(function)들에 argument로 전달될 수 있으며 다른 함수(function)들에서 반환 value들로 전달 받을 수 있다.
예를 들어 다른 함수(function) parameter로 취하고, 관련된 동작을 하는 다른 함수(function) wrapping한 다음에 새로운 함수(function) 반환하는 Python 함수(function) 작성하는 것도 가능하다. 이 새로운 조합은 original 함수(function)을 대신하여 사용될 수 있다.

>>> def decorate(func):
...     print("in decorate function, decorating", func.__name__)
...     def wrapper_func(*args):
...         print("Executing", func.__name__)
...         return func(*args)
...     return wrapper_func
...
>>> def myfunction(parameter):
...     print(parameter)
...
>>> myfunction = decorate(myfunction)
in decorate function, decorating myfunction
>>> myfunction("hello")
Executing myfunction
hello

장식자(decorator)는 이런 process syntactic sugar이며 한 줄로 다른 함수(function)을 포함시킬 수 있게 해준다. 장식자(decorator)는 위의 code와 정확하게 동일한 효과를 주지만 가독성을 더욱 깔끔하고 쉽게 해준다.
단순하게 장식자(decorator) 사용은 다른 함수(function)들을 wrapping하거나 “decorating”할 함수(function) 정의하는 부분을 작성한 다음 wrapping 될 함수(function) 정의하기 바로 전에 @ 문자 다음에 장식자(decorator) 사용하는 부분으로 구성된다. 장식자(decorator) 함수(function)은 다음과 같이 함수(function) parameter로 취해야 하며 함수(function)을 반환해야 한다.

>>> def decorate(func):
...     print("in decorate function, decorating", func.__name__)
...     def wrapper_func(*args):
...         print("Executing", func.__name__)
...         return func(*args)
...     return wrapper_func
...
>>> @decorate
... def myfunction(parameter):
...     print(parameter)
...
in decorate function, decorating, myfunction
>>> myfunction("hello")
Executing myfunction
hello

위의 예제에서 decorate 함수(function)장식자(decorator) 정의할 때 wrapping되는 함수(function)의 이름을 출력하고 마지막에는 wrapping되는 함수(function) 반환한다. myfunction @decorate를 사용하여 decorate된다. 장식자(decorator) 함수(function)의 수행이 완료되면 wrapping되는 함수(function)가 호출된다.
다른 함수(function) 내에서 하나의 함수(function) wrapping하기 위해 장식자(decorator) 사용하는 것은 다양한 용도에 사용되면서 간편함을 제공할 수 있다. Django와 같은 web framework들에서 장식자(decorator)는 함수(function) 수행하기 전에 반드시 사용자가 log in 되도록 하는데 사용될 수 있으며, graphics library들에서는 함수(function) graphics framework에 등록시키는데 사용될 수도 있다.

요약
Python에서 함수(function) 정의는 단순하지만 매우 유연하다. 함수(function) body 수행 중에 생성된 모든 변수(variable)들이 해당 함수(function) local 이더라도 global 구문을 사용하여 외부 변수(variable)들로 쉽게 접근이 가능하다.
Python 함수(function)은 매우 강력한 argument 전달 기능들을 제공한다.

argument들은 위치나 parameter 이름으로 전달될 수 있다.
함수(function) parameter들에 기본 값들을 제공할 수 있다.
함수(function) argument들을 tuple들에 수집할 수 있도록 하여 정해지지 않은 숫자의 argument들을 받을 수 있는 함수(function) 정의할 수 있게 해준다.
함수(function) argument들을 dictionary들에 수집할 수 있도록 하여 정해지지 않은 숫자의 parameter 이름으로 전달되는 argument들을 받을 수 있는 함수(function) 정의할 수 있게 해준다.

함수(function) Python에서 최상위 object이므로 변수(variable)에 할당되고, 변수에 사용되는 방식으로 접근할 수 있으며, decorate될 수도 있다. 또한 가독성을 가지도록 구조화된 code 작성을 위한 기본 building block이다. 특정 함수(function) 수행하는 code packaging 하는 것은 해당 code의 재사용을 용이하게 만들어준다. 또한 이는 code를 더욱 단순하고 이해하기 쉽게 만들어준다. 다음으로는 함수(function)들과 다른 object들을 module들로 packaging하는 것을 알아볼 것이다.