Source

JythonBook_KO / DefiningFunctionsandUsingBuilt-Ins.rst

Full commit

4장. 함수 정의하기 및 내장함수 사용하기

함수는 파이썬에서 작업의 기본 단위이다. 파이썬에서의 함수는 작업을 수행하여 결과를 반환한다. 이 장에서는 함수의 기초에서 시작하여 내장(built-in) 함수의 사용법을 알아볼 것이다. 내장 함수란 파이썬의 주요 함수로서, 이름 공간에 명시적으로 들여올 필요 없이 언제든지 사용할 수 있는 것들이다. 그 다음에는 함수를 정의할 수 있는 몇몇 다른 방법들, 예컨대 람다와 클래스 같은 것을 알아볼 것이다. 함수의 보다 발전된 형태인, 클로저와 생성기 함수도 살펴보도록 하겠다.

앞으로 살펴보겠지만, 함수는 만들어서 사용하기가 아주 쉽다. 파이썬에서는 함수를 점차적으로 개발해나가는 방식을 권장한다. 그것이 어떤 실질적인 이득이 있는가? 함수를 작성하다보면, 구문을 순서대로 나열해놓고 그저 콘솔에서 돌려보는 것에서 시작하는 것이 타당한 경우가 종종 있다. 혹은 편집기에서 짤막한 스크립트를 작성할 수도 있을 것이다. 예컨대 '이 API가 내가 예상했던 대로 동작할까'하는 의문이 생긴다면 그에 대한 검증을 해보자는 것이다. 왜냐면 콘솔이나 스크립트의 가장 높은 수준의 코드는 그저 하나의 함수처럼 동작하며, 이 코드를 나중에 함수의 몸체로 뽑아내어서, 함수라든지, 라이브러리 혹은 클래스에 속한 메서드 등으로 손쉽게 포장할 수 있기 때문이다. 이렇듯 손쉽게 개발할 수 있다는 점이 바로 파이썬을 사용함에 있어 유쾌한 측면이다. 또한 자이썬의 구현에 있어서도 말할 것도 없이, 어떠한 자바 라이브러리에 대하여든지 이러한 기법을 쉽게 적용할 수 있다.

염두에 두어야 할 요점은, 파이썬에서는 함수가 매우 중요하다는 것이다. 함수는 그저 다른 변수들처럼 이리저리 넘겨줄 수도 있으며, 매우 강력한 해법을 가져다준다. 이 장의 뒷부분에서 이러한 함수 사용에 대한 몇몇 예제를 살펴볼 것이다.

함수 구문 및 기초

함수는 보통 'def'라는 열쇳말, 함수의 이름, (존재하는 경우) 함수의 매개변수, 그리고 코드 몸체로 이루어진다. 다음 예제 함수부터 살펴보도록 하자.

예제 4-1.

def times2(n):
    return n * 2

이 예제에서, 함수의 이름은 times2이며 n을 매개변수로 취한다. 함수의 몸체는 단 한 줄이지만, 매개변수를 숫자 2로 곱하는 곱셈이 이루어진다. 그 결과는 변수에 저장하지 않고, 함수를 호출하는 코드로 바로 반환한다. 이 함수는 다음과 같은 방법으로 사용할 수 있다.

예제 4-2.

>>> times2(8)
16
>>> x = times2(5)
>>> x
10

일반적인 용법으로 함수의 정의를 매우 간단하게 처리할 수 있다. 그와 더불어, 함수 정의의 모든 부분에 있어서, 파이썬이 동적 언어라는 사실로부터 얻어지는 미묘한 힘이 있다. 보다 일반적이고 간단한 측면과, 보다 발전된 관점 양쪽을 통해 이러한 부분을 살펴볼 것이다. 이 장의 뒷부분에서는 함수를 만들어내는 또 다른 방법을 살펴볼 것이다.

def 열쇳말

무언가를 '정의하다define'를 나타내기 위해 'def' 열쇳말을 사용하는 것은 간편해 보이기도 하고, 정적 언어에서와 마찬가지로 함수를 정의하는 데 사용할 수 있다. 사실상 거의 대부분의 코드를 그런 식으로 작성해야 한다.

그렇지만, 함수 정의는 코드의 어떤 수준에서도 발생할 수 있으며 어느 시점에서든지 일어날 수 있다. C나 자바와 같은 언어에서와는 달리, 함수의 정의는 선언이 아니다. 파이썬에서의 함수 정의는 실행가능한 서술이다. 함수를 중첩시킬 수 있으며, 그에 대하여는 중첩된 유효 범위에 대하여 다룰 때 더 설명할 것이다. 또한 조건부로 함수를 정의하는 것과 같은 일도 할 수 있다.

따라서 다음과 같은 코드도 완전히 타당한 것이다.

예제 4-3.

if variant:
    def f():
        print "이렇게"
else:
    def f():
        print "혹은 저렇게"

언제 혹은 어디서 정의가 이루어지는가에 관계 없이, 위와 같이 변수까지도 포함하여, 함수 정의는 그 함수가 들어가는 모듈이나 스크립트의 나머지 부분과 같은 시간에 함수 개체로 컴파일된다는 점을 기억하자.

함수 이름 짓기

뒤에 가서 더 설명하겠지만, 내장 함수 dir을 실행하면 주어진 이름공간 - 기본적으로 모듈, 스크립트 혹은 작업 중인 콘솔 환경 - 내에서 정의된 이름에 대하여 알 수 있다. 앞에서 정의한 times2 함수를 예로 들면, 콘솔 이름공간에서 (최소한) 다음과 같은 것들을 볼 수 있을 것이다.

예제 4-4.

>>> dir()
['__doc__', '__name__', 'times2']

그 이름이 실제로 무엇과 관련bind되어 있는지도 알 수 있다.

예제 4-5.

>>> times2
<function times2 at 0x1>

(이 개체는 인트로스펙션* introspection*이 더욱 용이하다. 좀 더 살펴보려면 dir(times2)를 시도해보라.) 위의 예제에서 했던 것과 마찬가지로 우리는 함수의 이름을 dir 함수에 제공함으로써 그 함수를 참조할 수 있다. 그렇지만, 함수를 호출하고 어떤 작업을 수행시키기 위해서는, 이름 끝에 ()를 붙여야한다.

또한 함수는 언제든지 다시 정의할 수 있다.

예제 4-6.

>>> def f(): print "Hello, world"
...
>>> def f(): print "Hi, world"
...
>>> f()
Hi,world

콘솔에서 실행할 때 뿐만 아니라, 어떠한 모듈이나 스크립트에서도 마찬가지이다. 원래 버전의 함수 개체는, 그것에 대한 참조가 더 이상 이루어지지 않으면 결국 쓰레기 수집의 대상이 된다. 위의 예제에서는, f가 유일한 참조이기 때문에, 재지정rebind되는 즉시 쓰레기 수집 대상이 된다.

여기서 중요한 것은, 그저 이름을 재지정했을 뿐이라는 점이다. 하나의 함수 개체를 가리키던 이름이, 다른 것을 가리키도록 한 것이다. 우리는 단순히 다른 이름(동등한, 변수)을 times2에 지정함으로써 그것을 확인할 수 있다.

예제 4-7.

>>> t2 = times2
>>> t2(5)
10

이는 콜백callback 등을 위해 함수를 매개변수로서 전달하는 일을 아주 쉽게 할 수 있도록 해준다. 콜백은 함수에 의해 수행되는 기능으로, 말 그대로 작업 수행을 마친 다음에 자신을 호출했던 함수를 다시 호출하는 것이다. 함수의 매개변수에 대하여 좀 더 자세히 살펴보자.

함수의 매개변수와 함수 호출

함수를 정의할 때에는 그것이 취할 매개변수를 지정한다. 일반적으로 다음과 같은 것을 볼 수 있다. 다음과 같은 구문이 일반적이다.

def tip_calc(amt, pct)

이전에 언급했듯이, 함수호출은 함수 이름 뒤에 괄호를 삽입하면 된다. 예를 들어, 매개변수 a,b,c를 갖는 함수 x는 x(a,b,c)가 된다. 루비나 펄과 같은 다른 동적 언어와는 달리, 괄호의 사용은 필수적이다(함수의 이름은 다른 이름과 동일하기 때문이다).

우리는 지금껏, 개체란 그 형type이 굳게 정해지는 것으로만 알고 있었다. 그러나, 파이썬에서는 이름에 대하여 형을 정하지 않는 것이 일반적이듯 함수의 매개변수에 대해서도 형이 정해지지 않는다. 이는 임의의 매개변수가 어떠한 유형의 개체든지 참조할 수 있음을 의미한다.

times2 함수를 가지고 실험해보자. * 연산자는 숫자에 대한 곱셈을 의미할 뿐 아니라, 순서형(문자열이나 목록)을 반복하는 의미도 갖는다. 그러므로 times2 함수는 다음과 같이 사용할 수 있다.

예제 4-8.

>>> times2(4)
8
>>> times2('abc')
'abcabc'
>>> times2([1,2,3])
[1, 2, 3, 1, 2, 3]

파이썬의 모든 매개 변수는 참조에 의해 전달된다. 그러한 점은 자바에서의 개체 매개변수와 동일하다. 하지만, 자바에서는 원시 자료형을 값으로 전달하는데 비해, 파이썬에는 그러한 속성은 없다. 파이썬에서는 모든 것이 개체인 것이다. 불변immutable 개체에는 변경이 일어날 수 없으므로, 문자열을 함수로 넘겨주고 변경하는 경우에는 문자열의 복사본이 만들어지며, 변경사항은 그 사본에 대하여 적용된다는 점을 반드시 기억하자.

예제 4-9.

# 다음의 함수는 문자열 텍스트를 변경하기 위해 문자열의 사본을
# 만든 다음에 그것을 변경한다. 원래의 문자열은
# 불변이기 때문에 손댈 수 없다.

>>> def changestr(mystr):
...     mystr = mystr + '_changed'
...     print 'The string inside the function: ', mystr
...     return
>>> mystr = 'hello'
>>> changestr(mystr)
The string inside the function:  hello_changed
>>> mystr
'hello'

함수도 역시 개체이며, 매개변수로 넘겨줄 수도 있다.

예제 4-10.

# 두 개의 값과 수학 함수를 매개변수로 받는 함수를 정의
>>> def perform_calc(value1, value2, func):
...     return func(value1, value2)
...
# 넘겨줄 수학 함수를 정의
>>> def mult_values(value1, value2):
...     return value1 * value2...
>>> perform_calc(2, 4, mult_values)
8
# 또 다른 함수를 정의하여 넘겨줌
>>> def add_values(value1, value2):
...     return value1 + value2
...
>>> perform_calc(2, 4, add_values)
6
>>>

만약 두 개 이상의 인수가있다면, 위치 매개변수를 이용하는 것보다 이름 지어진 변수를 기준으로 함수를 호출하는 것이 더 좋을 것이다. 이렇게 하면 보다 튼튼한 코드를 만들 수 있다. 즉, 함수 draw_point(x,y)가 있다고 했을때, draq_point(x=10,y=20)와 같은 식으로 호출할 수도 있다.

기본값을 정해두면 함수 호출을 보다 단순화할 수 있다. 기본값은 함수를 정의할 때 변수=기본값 형식으로 지정할 수 있다. times2 함수의 예를 먼저 살펴보고 이를 일반화해보자.

예제 4-11.

def times_by(n, by=2):
    return n * by

이 함수는 하나의 인수만 가지고 호출할 때는 times2와 동일하다. 두 번째 인수 by의 기본값을 사용하기 때문이다.

개발하면서 한가지 기억하고있어야 할 것이 있다. 기본값은 정확하게 한 번, 함수가 정의되었을 때 초기화된다. 그것은 숫자, 문자열, 튜플, 동결집합frozensets 등의 개체와 같은 변경 불가능한 값에 대해서는 타당하다. 그러나 만약 초기값이 변경가능mutable하다면, 초기값이 제대로 사용되고 있는지 확인해야 한다. 따라서 사전을 공유 캐시로 사용하는 것은 일리가 있다. 하지만, 목록에 대해서는, 호출될 때에 빈 목록으로 초기화될 것으로 생각할 수 있지만, 그런 식으로 작동하지는 않는다. 만약 목록을 빈 목록으로 초기화 하고 싶으면 코드 속에 명시적으로 작성해주어야한다. 최선의 방법으로, 변수의 기본값으로는 변경 가능한 개체보다는 None을 사용하고, 함수 몸체의 시작부분에서 변수 = None인 경우를 검사하고, 거기에서 변수 값으로 변경 가능 개체를 설정하라.

마지막으로, 함수는 *args, **kwargs를 이용해서 딱 정해지지않은 개수의 인수도 취할 수 있다. 이러한 매개 변수 이름 (args 와 kwargs)은 약속에 의한 것이므로 함수에 사용할 때 어떤 이름이라도 사용해도 좋다. *와 ** 표식은 이 기능이 반드시 쓰여야하는지 결정하는 데에 쓰인다. 단일 * 인수는 순서형 변수를 전달할 때 사용하고, 더블 ** 인수는 이름과 값으로 된 사전을 전달할 때 사용한다. 인수가 이러한 유형(순서형, 사전형) 중 하나가 지정된 경우, 단일 * 인수를 사용해야 한다. 또한, 두 개 짜리 **는 * 하나를 따라야 한다.

숫자들로 이루어진 순서형 변수를 받는 함수를 만들어보자.

예제 4-12.

def sum_args(*nums):
    return sum(nums)

목록을 사용하여 함수를 호출한다.

>>> seq = [6,5,4,3]
>>> sum_args(*seq)
18
# *를 사용하지 않고 함수를 호출할 수 있다
>>> sum_args(1,2,3,4)
10

재귀 함수 호출

함수 내부에서 함수 자신을 호출하는 경우를 흔히 볼 수 있다. 함수 호출의 이러한 유형은 재귀 함수 호출로 알려져 있다. 주어진 인자로 팩토리얼(계승(階乘) : 1~n 까지의 모든 자연수를 곱한 것; 3! = 1*2*3 )을 구하는 함수를 보자. 이 함수는 인수가 0 또는 1의 값을 가질 때까지 1씩 감소된 인자를 전달하며 함수 자신을 호출한다.

예제 4-13.

def fact(n):
    if n in (0, 1):
        return 1
    else:
        return n * fact(n - 1)

CPython과 같이 자이썬도 궁극적으로 스택기반이라는 점을 유념해야 한다. 스택은 후입선출 방식으로 데이터가 추가, 제거 되는 메모리 영역이다. 재귀 함수가 자기 자신을 너무 많이 호출하면 스택영역을 전부 소진해 버려서 OutOfMemoryError가 날 수 있다. 그렇기 때문에 개발할 때 깊은 수준의 재귀호출을 사용하는 것은 주의를 요한다.

함수 몸체

이 절에서는 함수의 몸체를 구성하고 있는 여러 구성요소들을 파헤친다. 함수의 몸체는 작업을 수행하는 부분이다. 다음의 몇 절을 통하여 여러분은 함수가 여러가지 다른 부분으로 구성될 수 있다는 것을 알게 될 것이다.

함수 문서화

먼저 여러분은 함수에 대한 문서화 문자열docstring을 정의해야 한다. 문서화 문자열이 존재하는 경우에는, 함수 몸체의 처음에 오게 된다.

예제 4-14.

def times2(n):
    """n이 매개변수로 주어지면 n*2를 반환"""
    return n * 2

1장에서 언급한 바와 같이, 문서화 문자열이 여러 줄이 아니라 하더라도, 관례에 따라 큰 따옴표 세 개를 사용한다. 여러 줄이라고 하면, 이것이 여러분에게 권장하는 방법이다. 자세한 내용은 PEP 257(www.python.org/dev/peps/pep-0257)을 살펴보기 바란다.

예제 4-15.

def fact(n):
    """n의 계승을 반환.
       n의 계승을 재귀적으로 계산.
       음수인지, 스택오버플로우가 일어나는지 검사하지 않음.
       조심해서 사용할것!
    """
    if n in (0, 1):
        return 1
    else:
        return n * fact(n - 1)

그러한 문서화 문자열에서 첫머리의 들여쓰기를 제거하면 그 개체의 __doc__ 속성이 된다. 문서화 문자열은 모듈과 클래스에 대해서도 정확히 동일한 방식으로 작동한다.

문서화 문자열은 help 내장함수를 사용하여 볼 수 있으며, 이클립스 용 PyDev나 NetBeans 용 nbPython과 같이 자동완성 기능을 가진 여러 통합개발환경에서도 볼 수 있다.

예제 4-16.

>>> help(fact)
Help on function fact in module __main__:

fact(n)
    Returns the factorial of n
>>>

값 반환

모든 함수는 값을 반환한다. times2에서, 우리는 return 문을 통하여 값을 가진 채로 함수를 종료하였다. 함수는 쉽게 튜플 또는 다른 구조를 반환하여 한 번에 여러 값을 반환할 수 있다. 다음은 하나 이상의 값을 반환하는 함수의 간단한 예제이다. 이 경우, 팁 계산기는 두 비율 값에 따라 팁을 계산한 결과를 반환한다.

예제 4-17.

>>> def calc_tips(amount):
...     return (amount * .18), (amount * .20)
...
>>> calc_tips(25.25)
(4.545, 5.050000000000001)

함수는 언제든지 반환할 수 있으며, 또한 어떠한 개체든지 반환값으로 할 수 있다. 따라서 다음과 같은 함수를 작성하는 것도 가능하다.

예제 4-18.

>>> def check_pos_perform_calc(num1, num2, func):
...     if num1 > 0 and num2 > 0:
...         return func(num1, num2)
...     else:
...         return 'Only positive numbers can be used with this function!'
...
>>> def mult_values(value1, value2):
...     return value1 * value2
...
>>> check_pos_perform_calc(3, 4,mult_values)
12
>>> check_pos_perform_calc(3, -44, mult_values)
'Only positive numbers can be used with this function!'

만약 return 문이 사용되지 않은 경우에는 None이 반환된다. 자바의 void 메서드에 상응하는 것은 존재하지 않는데, 모든 파이썬 함수는 값을 반환하기 때문이다. 어쨌든, 파이썬 콘솔에서는 None을 보여주지 않으므로, 무엇이 반환되었는지 확인하고 싶으면 명시적으로 print해야 한다.

예제 4-19.

>>> do_nothing()
>>> print do_nothing()
None

변수 제시

함수는 변수와 같이 새로운 이름들에 대한 범위를 제시한다. 함수에서 만들어진 모든 이름은 그 범위 내에서 볼 수 있다. 다음 예제에서, sq 변수는 함수 선언 자체의 범위 내에서 정의된다. 우리가 함수의 밖에서 그것을 사용하려고하면 오류가 발생한다.

예제 4-20.

>>> def square_num(num):
...    """ Return the square of a number"""
...     sq = num * num
...     return sq
...
>>> square_num(35)
1225
>>> sq
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError:  name 'sq' is not defined

예제 4-21.

>>> sq = 0
>>> def square_num(n):
...     global sq
...     sq = n * n
...     return sq
...
>>> square_num(10)
100
>>> sq
100

기타 구문

함수의 몸체에는 무엇이 올 수 있을까? 다양한 것들이 올 수 있는데 앞으로 이 책에서 다루게 될 것이다. 함수의 범위 내에서, 함수 혹은 클래스를 정의하거나 심지어는 import를 사용할 수도 있다.

특히 import와 같이 잠재적으로 비용이 높은 연산을 수행하는 것을 최소화함으로써, 여러분의 애플리케이션을 기동하는 데 걸리는 시간을 줄일 수 있다. 전혀 필요하지 않을 수도 있다.

이 규칙에는 두 가지 예외가 있다. 두 경우 모두, 이 문장들은 모듈의 시작 부분에 위치해야하는데, 이는 자바와 같은 정적 언어에서 볼 수 있는 것과 유사하다.

컴파일러 지시어. 파이썬은 from __future__ import X와 같은 도발적인 문법을 통하여 컴파일러 지시어의 제한된 집합을 지원한다. PEP 236을 참조하라. 이러한 것들은 일반적으로 마이너 개정(예를 들어 2.5에서 2.6으로 변경되는 경우)을 통하여 사용가능하게 된다. 또한, from __future__ import braces와 같은 이스터 에그가 들어가는 경우도 종종 있다. (처음에 수행해보면 편안할 것이므로, 콘솔에서 시험해보기 바란다.)

소스 인코딩 선언. 기술적으로는 구문이라 할 수 없지만 - 특별하게 처리되는 주석이다 - 첫 줄이나 두 번째 줄에 들어있어야만 한다.

빈 함수

비어있는 함수를 정의하는 것도 가능하다. 왜 아무것도 하지 않는 함수가 존재하는가? 수학에서와 마찬가지로, 아무 일도 하지 않는 연산, 즉 "더하기 0"이라든지 "곱하기 1"과 같은 것이 유용하기 때문이다. 이러한 것들이 존재함으로해서 특별한 경우를 방지할 수 있다. 마찬가지로, empty_callback에서 보듯이, 콜백 함수를 기술해야만 하지만, 실제로 아무 일도 할 필요가 없는 경우가 있을 수 있다. 빈 함수를 전달함으로써 - 혹은 이것을 기본으로 함으로써 - 우리는 API를 단순화할 수 있다. 빈 함수라 할지라도 몸체는 있어야 하는데, 여기에 pass 문을 사용할 수 있다.

예제 4-22.

def do_nothing():
    pass # 코드 몸체가 비어 있음을 이렇게 표시한다.

# 혹은 다음 예제와 같은 문서화 문자열을 넣어도 된다.
def empty_callback(*args, **kwargs):
    """Use this function where we need to supply a callback,
       but have nothing further to do.
    """

궁금한 독자를 위한 기타 정보

이미 알다시피, 자이썬은 인터프리트 언어이다. 이는, 우리가 자이썬 어플리케이션을 위하여 작성하는 파이썬 코드는 최종적으로는 프로그램이 실행될 때 자바 바이트코드로 컴파일됨을 의미한다. 그러므로 이러한 코드가 자바 바이트코드로 해석될 때 어떤 일이 일어나는지 이해하는 것은 자이썬 개발자에게 유용하다. 함수는 자바의 어떤 것과 닮아보이는가? 함수는, __call__ 메서드를 지원하는 PyObject라는 개체의 인스턴스들이다. 추가적인 인트로스펙션이 사용 가능하다. 만약에 어떤 함수가 Python으로 작성된 표준 함수라면, 그것은 PyFunction 클래스에 속할 것이다. 내장 함수는 PyBuiltinFunction 클래스에 속할 것이다. 그렇지만 여러분의 코드에서도 그럴 것이라고 가정해서는 안된다. 왜냐하면 다른 많은 개체들도 __call__ 함수 인터페이스를 지원하며, 이것들은 여러 단계에 걸쳐 대리(proxy)되었을 가능성이 있기 때문이다. 여러분은 그저 그것이 PyObject일 것이라고 가정할 수 있을 따름이다. 자세한 정보는 자이썬 위키를 참조하기 바란다. 또한 jython-dev 메일링 리스트에 보다 구체적인 질문을 할 수 있다.

내장 함수

내장 함수built-in function란 항상 파이썬 이름 공간에 있는 함수를 말한다. 즉, 이러한 함수 - 및 내장된 예외, 부울 값, 그리고 몇몇 개체 - 들이야말로 전역으로 정의된 이름이다 자바에 비유하자면, java.lang에 있는 클래스와 같은 것이라 할 수 있다. 그렇지만, 아무리 간단한 명령행 스크립트라고 해도 인자를 분석하거나 표준 입력을 읽어들여야 하므로, 내장된 것들만으로는 부족한 것이 사실이다. 이러한 경우에는 sys를 들여와야 할 것이다. 그리고 자이썬에 있어서는, 적당한 자바 클래스와 함께 java도 수입할 것이다. 그렇다 하더라도 내장 함수가 모든 파이썬 코드에서 사용하는 핵심 기능인 것은 분명하다. 모든 내장 함수를 다루는 문서는 분량이 많다. 그러나, 그것은 이 책의 부록 C에 실려있다. 내장 함수를 사용할 때, 혹은 어느 내장 함수를 사용해야할지 알고 싶을 때에 부록 C를 참고하면 편리할 것이다.

함수를 정의하는 다른 방법들

'def' 열쇳말을 사용하는 것 이외에도 함수를 정의하는 방법이 있다. 다음과 같이, 함수를 정의하는 몇 가지 다른 방법이 있다.

  • 람다 함수 : 'lambda' 함수. 'lambda' 열쇳말을 사용하여 이름 없는 함수를 만들 수 있다. 어떤 사람들은 람다가 자리를 적게 차지하고, 특히 콜백에서 쓸 때 그러하다는 이유로 람다를 선호한다.
  • 클래스 : 또한, 우리는 또한 인스턴스개체가 일반 함수처럼 보이는 클래스를 가지는 개체를 만들 수 있다. __call__ 프로토콜을 지원하는 개체. 자바 개발자라면 낯이 익을 것이다. 클래스는 Callable 또는 Runnable 같은 단일 메서드 인터페이스를 구현한다.
  • 바운드 메서드: x.a()를 호출하는 것 대신, 매개 변수로 x.a를 전달하거나, 다른 이름으로 바인드 할 수 있다. 그러면 이 이름을 호출할 수 있다.메서드의 첫 번째 매개 변수가 바인드된 개체 - 객체지향의 용어로 메서드의 수신자(receiver)라고 한다 - 로 전달된다. 이런 식으로 콜백을 손쉽게 만들 수 있다. (자바에서라면 당연히 개체를 그냥 전달한 다음 call이라든지 run과 같은 적당한 메서드에 대한 콜백 호출을 하도록 했을 것이다.)

람다 함수

서두에서 밝힌 것과 같이, 람다 함수는 익명의 함수이다. 달리 말해, 람다 함수에는 굳이 이름이 필요하지 않다. 짧막한 코드를 원하거나, 단 한 번만 사용할 함수라서 이름을 붙이고 싶지 않을 때에 쓸모가 있을 것이다. 람다함수는 보통 다른 코드 줄 안에 쓰여지며, 람다 함수의 몸체는 당연히 매우 짧다. 람다 함수는 다음과 같이 구성된다. lambda <<인자(들)>> : <<함수 몸체>> 람다 함수는 다른 함수처럼 인자를 받아, 함수 몸체 내에서 이러한 인수를 사용한다. 또한, 여느 파이썬 함수에서와 마찬가지로 항상 값이 반환된다. 람다가 어떤 식으로 동작하는지 더 잘 이해하기 위해 간단한 람다 함수를 살펴보도록 하자.

예제 4-23.두 개의 문자열(여기서는 이름과 성)을 결합하는 람다 함수 예제.

>>> name_combo = lambda first,last: first + ' ' + last
>>> name_combo('Jim','Baker')
'Jim Baker'

위의 예제에서는 함수에 이름을 부여하였다. 그렇지만, 람다 함수는 다른 코드 안으로(inline) 들어갈 수 있다. 때로 람다 함수는 내장함수라 불리는 다른 함수의 컨텍스트 내에서 사용된다.

생성기 함수

생성기generator는 6장에서 다룰 반복자iterator의 예가 되는 특별한 함수이다. 생성기는 특별한 메서드인 next의 호출에 의하여 다음 지점으로 진행한다. 이것은 보통 묵시적으로 이루어지는데, 보통은 루프를 통하거나, 생성기를 포함한 반복자를 받아들이는 소비자 함수를 통하여 이루어진다. 생성기는 yield 문을 사용하여 값을 반환한다. yield 문을 만날 때마다 현재의 이터레이션은 중단되고 값이 반환된다. 생성기는 그만 둔 곳을 기억하는 능력이 있다. next()가 호출될 때마다, 생성기는 그만두었던 곳에서 하던 일을 재개한다. 생성기가 종료되고 나면 StopIteration 오류가 일어난다. 다음 두 절을 통해, 생성기의 작동 원리를 살펴보도록 하겠다. 생성기를 만들고 사용하는 여러 가지 예를 볼 수 있을 것이다.

생성기 정의하기

생성기 함수는 한 곳 이상의 포기 지점yield point을 갖도록 작성하며, 그러한 지점들은 yield 문을 사용하여 표시한다. 앞에서 언급한 바와 같이, yield 문을 만날 때마다, 값이 반환된다.

예제 4-24.

def g():
    print "before yield point 1"
    # 생성기는 yield 문을 만나면 값을 반환한다
    yield 1
    print "after 1, before 2"
    yield 2
    yield 3

앞의 예제에서, 생성기 함수 g()는 처음 yield 문을 만나면 중단되며 값을 반환한다. 이 경우에는 1이 반환된다. 다음번에 g.next()가 호출되면, 다음번의 yield 문을 만날 때까지 생성기가 계속된다. 이번에는 다른 값인 2가 반환될 것이다. 생성기가 실제로 동작하는 모습을 보자. 참고로, 생성기 함수를 호출하는 것은 단지 생성기를 만들어낼 뿐이지, yield의 원인이 되는 것은 아니다. 첫 yield로부터 값을 얻어내려면, next()를 호출해야 한다.

예제 4-25.

# 생성기를 만들기 위하여 함수를 호출
>>> x = g()
# yield로부터 값을 얻기 위해 next()를 호출
>>> x.next()
before the yield point 1
1
>>> x.next()
after 1, before 2
2
>>> x.next()
3
>>> x.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

이 생성기의 또 다른 보다 유용한 예를 살펴보자. 다음 예제에서, step_to() 함수 생성기는 주어진 요소에 따라 증가하는 생성기이다. 생성기는 0부터 시작하고, next()가 호출될 때마다 증가한다. 이 생성기는 정지 인수가 제공하는 값에 도달하면 작동이 중단된다.

예제 4-26.

>>> def step_to(factor, stop):
...     step = factor
...     start = 0
...     while start <= stop:
...         yield start
...         start += step
...
>>> for x in step_to(1, 10):
...     print x
...
0
1
2
3
4
5
6
7
8
9
10
>>> for x in step_to(2, 10):
...     print x
...
0
2
4
6
8
10
>>>

yield 문이 함수의 범위 내에서 보이면, 그 함수는 생성기 함수인 것처럼 컴파일된다. 여타의 함수들과는 달리, return 문은 "끝났다"라는 의미로만 사용할 수 있으며, 이는 어떠한 값도 반환하지 않고 생성기를 종료한다. return을 for 되풀이 혹은 while 되풀이에서의 break와 같은 것이라고 생각할 수 있다. step_to 함수를 조금만 바꾸어서 factor가 stop보다 작은 값을 가지는지 점검, 보증을 하도록 만들어보자. factor가 stop보다 크거나 같을 경우에 생성기를 종료하도록 하기 위하여 return 문을 추가할 것이다.

예제 4-27

>>> def step_return(factor, stop):
...     step = factor
...     start = 0
...     if factor >= stop:
...         return
...     while start <= stop:
...         yield start
...         start += step
...
>>> for x in step_return(1,10):
...     print x
...
0
1
2
3
4
5
6
7
8
9
10
>>> for x in step_return(3,10):
...     print x
...
0
3
6
9
>>> for x in step_return(3,3):
...     print x
...

만약 여러분이 인자를 return하려고 하면 구문 오류가 발생할 것이다.

예제 4-28.

def g():
    yield 1
    yield 2
    return None

for i in g():
    print i

SyntaxError: 'return' with argument inside generator

많은 유용한 생성기가 명시적이든 아니든 실제로 종료되지 않고 yield 표현식 주변에서 무한반복을 한다. 생성기는 필연적으로 프로그램이 수명이 다할 때까지 next()가 호출될 때마다 동작하게 될 것이다.

예제 4-29. 무한 루프를 사용하여 표현한 생성기 의사 코드

while True:
    yield stuff

이 코드는 작동할 수 있는데, 그 이유는 사용하는 생성기에 대한 참조가 마지막까지 끝나야 생성기 객체가 쓰레기 수집이 되기 때문이다. 함수 객체가 스스로를 구현하기 위한 절차를 사용한다는 사실은 그다지 문제가 되지 않는다.

생성기 표현식

생성기 개체를 만들 수 있는 다른 방법으로는 생성기 표현식이 있다. 이것은 생성기 함수와 같지 않음에 유의하기 바란다! 생성기 함수가 호출되었을 때 yield하는 것에 상응한다. 생성기 표현식은 기본적으로 익명의 생성기를 만들 수 있다.

예제 4-30.

>>> x = (2 * x for x in [1,2,3,4])
>>> x
<generator object at 0x1>
>>> x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'generator' object is not callable
Let’s see this generator expression in action:
>>> for v in x:
...     print v
...
2
4
6
8
>>>

대체로 생성기 표현식은 생성기 함수에 비하여 간소하지만 융통성이 떨어지는 경향이 있다. 생성기 표현식은 간결한 방식으로 일을 해내는 데 유용하다.

이름 공간, 중첩된 유효 범위 그리고 클로저

참고로 여러분의 함수 안으로 다른 이름 공간을 가져올 수 있다. 함수의 몸체 내에서 직접 import 문을 포함시키는 것이 가능하다. 이 경우 가져오기는 함수의 문맥 내에서만 유효하게 된다. 예를 들어, 다음과 같은 A와 B에 대한 가져오기는 f()의 문맥 내에서만 유효하다.

예제 4-31.

def f():
    from NS import A, B

언뜻 보면, 함수 내에 import 문을 포함하는 것은 불필요한 것처럼 보일 수 있다. 그러나 함수도 하나의 개체라는 점을 생각해본다면 이것도 일리가 있다. 파이썬에서는 변수와 같은 다른 개체들과 마찬가지로 함수를 전달할 수도 있는 것이다. 이전에 언급했듯이, 심지어는 함수를 다른 함수의 인수로 전달 할 수도 있다. 함수 이름공간에서는 함수를 별도의 코드 조각으로 다룰 수 있도록 해준다. 애플리케이션의 여러 다른 장소에서 사용되는 함수들은 각각의 모듈에 저장되곤 한다. 이러한 모듈은 프로그램 내의 필요한 곳으로 반입된다. 함수는 또한 서로 중첩됨으로써 유용한 해법을 만들어낸다. 함수는 자신의 이름공간을 가지므로, 다른 함수 내에서 정의된 어느 함수라도 부모 함수 안에서만 유효하다. 더 들어가기 전에 이에 대한 간단한 예제를 살펴보도록 하자.

예제 4-32.

>>> def parent_function():
...     x = [0]
...     def child_function():
...         x[0] += 1
...         return x[0]
...     return child_function
…
>>> p = parent_function()
>>> p()
1
>>> p()
2
>>> p()
3
>>> p()
4

그다지 쓸모가 없는 이 예제는, 함수 중첩에 대한 개념을 이해하도록 돕기 위한 것이다. 보다시피, parent_function은 child_function이라는 이름의 함수를 포함하고 있다. 이 예제에서 parent_function은 child_function를 반환한다. 이 예제에서 만들어 낸 것은 바로 간단한 클로저closure 함수이다. 이 함수는 호출될 때마다 안쪽의 함수를 실행시키며, 폐쇄된 범위 내에서만 사용가능한 변수 x를 증가시킨다.

이와 같은 방식으로 클로저를 사용하는 것은 자이썬에 자바를 통합하는 데에 유용할 수 있다. 파이썬 모듈에 대해서와 마찬가지로, 함수 범위 내로 자바 클래스를 들여오는 것이 가능하다. 함수 호출내에서 import를 하는 것은, 함수 A는 함수 B를 들여오고 함수 B는 함수 A를 들여오는 것과 같은 순환적 들여오기를 피하는 데에 유용할 때가 있다. 함수 내에 들여오기를 사용함으로써 꼭 필요한 곳에서만 들여오기를 사용할 수 있다. 자이썬 내에서 자바를 사용하는 것에 대해서는 10장에서 더 배울 것이다.

함수 장식자

장식자decorator는 함수를 변형시키는 방법을 기술하는 데에 편리하다. 그것들은 본질적으로, 그것들이 장식하는 함수의 행위를 강화하는 메타 프로그래밍 기법이다. 함수 장식자란, 이미 정의된 함수를 다른 함수를 장식하는 데에 사용하는 것으로, 장식자를 프로그램하는 것은 기본적으로 장식된 함수가 그 함수로 전달될 수 있도록 해준다. 간단한 예를 살펴보자.

예제 4-33.

def plus_five(func):
    x = func()
    return x + 5

@plus_five
def add_nums():
    return 1 + 2

이 예제에서는 add_nums() 함수는 plus_five() 함수로 장식되어 있다. 이것은 plus_five 함수로 add_nums 함수를 전달하는 것과 같은 효과가 있다. 달리 말해, 이러한 기법을 사용하기 쉽게 만든 문법적인 감초와 같은 것이 바로 장식자이다. 다음의 코드는 위에서 살펴본 장식과 동일한 기능을 한다.

예제 4-34.

add_nums = plus_five(add_nums)

사실 이렇게 하면 add_nums는 더이상 함수가 아니며, 정수가 되어버린다. plus_five 로 장식을 하면 더이상 add_nums()를 호출 할수는 없고 오로지 정수로서 참조만 할수 있다. 위에서 본것과 같이 add_nums 는 import 시점에 plus_five로 전달 된다. 보통 우리는 add_nums를 계속 호출 가능한 함수로 가지고 싶을 것이다. 이 예제를 좀더 쓸만하게 만들기 위해서는, add_nums를 다시 호출 가능하도록 만들고, 추가되는 숫자를 변경할 수 있는 능력을 부여하면 좋을 것이다. 그렇게 하려면, 장식된 함수로부터 인자를 취하는 안쪽 함수를 포함하도록 장식 함수를 약간 손볼 필요가 있다.

예제 4-35.

def plus_five(func):
    def inner(*args, **kwargs):
        x = func(*args, **kwargs) + 5
        return x
    return inner

@plus_five
def add_nums(num1, num2):
    return num1 + num2

이제 우리는 다시 한번 add_nums() 함수를 호출할 수 있으며, 우리는 또한 그것에 두 개의 인수를 전달할 수 있다. 왜냐하면 그것은 전달되는 plus_five 함수로 장식된 후에 두 개의 인자가 합산되어 숫자 5가 그 합계에 더해질 것이기 때문이다. 그런 다음 결과가 반환된다.

예제 4-36.

>>> add_nums(2,3)
10
>>> add_nums(2,6)
13

함수 장식자의 기초를 다루었으니 이제 개념을 이해하기 위한 깊이 있는 예제를 살펴볼 차례이다. 다음 장식자 함수 예제에서 우리는 이전에 만든 tip_calculator 함수와 판매세금 계산을 추가하는 상상치 못한 방법을 보게 될것이다. 알다시피 원래의 calc_bill 함수는 금액들의 나열, 그러니까 청구서의 각 항목들의 가격을 취한다. calc_bill 함수는 단순히 금액을 합하여 값을 반환한다. 예제에서는, sales_tax 장식자를 함수에 적용함으로써, 모든 금액에 대한 합계를 계산하여 반환할 뿐만 아니라 표준 세율을 적용하고 세 금액과 총 금액도 반환하도록 함수를 변형한다.

예제 4-37.

def sales_tax(func):
    ''' Applies a sales tax to a given bill calculator '''
    def calc_tax(*args, **kwargs):
        f = func(*args, **kwargs)
        tax = f * .18
        print "Total before tax: $ %.2f" % (f)
        print "Tax Amount: $ %.2f" % (tax)
        print "Total bill: $ %.2f" % (f + tax)
    return calc_tax

@sales_tax
def calc_bill(amounts):
    ''' Takes a sequence of amounts and returns sum '''
    return sum(amounts)

장식자 함수는 안쪽 함수를 포함하며, 안쪽 함수는 두 개의 인자 - 매개변수의 시퀀스와 키워드 args의 사전 - 를 받는다. 우리는 원래의 함수로 전달되는 인자뿐만 아니라 장식자 함수 내에 적용되도록 장식에서 호출했을 때 우리는 원래의 함수에 해당 인수를 전달해야 한다. 이 경우, 우리는 금액의 시퀀스를 calc_bill에 전달하고자 하며, *args와 **kwargs 인자를 함수에 전달함으로써 금액 시퀀스가 장식자를 통하여 전달되었음을 보장할 수 있다. 장식자 함수는 다음과 세금 합계 금액에 대한 간단한 계산을 수행하고 결과를 출력한다. 실제 예제를 보자.

예제 4-38.

>>> amounts = [12.95,14.57,9.96]
>>> calc_bill(amounts)
Total before tax: $ 37.48
Tax Amount: $ 6.75
Total bill: $ 44.23

장식을 하고 있을 때에 장식자 함수에 인자를 전달하는 것도 가능하다. 그렇게 하려면, 장식자 함수 내에 다른 함수를 중첩시켜야 한다. 바깥쪽 함수는 장식자 함수로 전달되는 매개변수를 취하고, 안쪽 함수는 장식된 함수를 취하며, 가장 안쪽의 함수가 실제로 일을 한다. 또 다른 스핀을 팁 계산기 예제에서 하고, 팁 계산을 calc_bill 함수에 적용할 장식자를 생성한다.

예제 4-39.

def tip_amount(tip_pct):
    def calc_tip_wrapper(func):
        def calc_tip_impl(*args, **kwargs):
            f = func(*args, **kwargs)
            print "Total bill before tip: $ %.2f" % (f)
            print "Tip amount: $ %.2f" % (f * tip_pct)
            print "Total with tip: $ %.2f" % (f + (f * tip_pct))
        return calc_tip_impl
    return calc_tip_wrapper

이제 장식자 함수가 동작하는 것을 살펴보자. 백분율 금액을 장식에 전달하면 그것이 장식자 함수에 적용됨을 알게 될 것이다.

예제 4-40.

>>> @tip_amount(.18)
... def calc_bill(amounts):
...     ''' Takes a sequence of amounts and returns sum '''
...     return sum(amounts)
...
>>> amounts = [20.95, 3.25, 10.75]
>>> calc_bill(amounts)
Total bill before tip: $ 34.95
Tip amount: $ 6.29
Total with tip: $ 41.24

팁 백분율을 구분할 수 있다는 점을 제외하면, 장식자를 통하여 판매 세금 계산기에서와 비슷한 결과를 얻었다. 목록의 모든 금액이 합산된 후에 팁이 적용된다. 장식자 @ 구문을 사용하지 않을 경우 어떻게 되는지 간단히 살펴보자.

예제 4-41.

calc_bill = tip_amount(.18)(calc_bill)

import 시에, tip_amount() 함수는 팁의 백분율과 calc_bill 함수를 둘 다를 매개변수로 취하여, 그 결과는 새로운 calc_bill 함수가 된다. 장식자를 포함함으로써, 실제로는 tip_amount(.18)에서 반환한 함수를 가지고 calc_bill을 장식한다. 좀 더 크게 보아, 이러한 장식자 해법을 완전한 애플리케이션에 적용한다면, 키보드 입력으로 팁 백분율을 받아서 그것을 예제에서 보인 것처럼 장식자에 전달할 수 있을 것이다. 팁 금액은 상황에 따라 변동될 수 있는 변수가 될 것이다. 끝으로, 보다 복잡한 장식자 함수를 다룬다면, 본래 장식된 함수를 전혀 건드리지 않고도 함수 내의 동작을 변경할 수 있을 것이다. 장식자는 우리의 코드를 더욱 융통성 있고 관리하기 쉽게 해주는 쉬운 방법이다.

코루틴

코루틴coroutine도 yield 문을 사용하며, 그 점에서 종종 생성기 함수와 비교된다. 그러나, 코루틴은 기능 측면에서 정확히 생성기의 반대이다. 코루틴은 yield 문을 사실상 표현식으로 취급하며, 데이터를 반환하는 것이 아니라 받아들인다. 코루틴은 첫인상이 마치 daunting topic과 같아서 간과되는 수가 많다. 그러나, 코루틴과 생성기가 서로 다르다는 점을 일단 받아들이고 나면 그 원리를 이해하기가 수월하다.

코루틴은 데이터를 받아서 그것으로 무언가를 하는 함수이다. 간단한 코루틴 예제를 살펴본 다음 그 기능을 찬찬히 뜯어보자.

예제 4-42.

def co_example(name):
    print 'Entering coroutine %s' % (name)
    my_text = []
    while True:
        txt = (yield)
        my_text.append(txt)
        print my_text

위는 매우 단순한 코루틴 예제이다. 그것은 코루틴의 "이름"으로 값을 받아들이다. 그런 다음 그것은 텍스트의 문자열을 받으며, 텍스트의 문자열이 코루틴으로 보내질 때마다, 그것은 목록에 추가된다. yield 문은 사용자로부터 텍스트가 입력되는 지점이다. 그것은 txt 변수에 할당된 후 코루틴을 처리한다. 그 코루틴이 존재하는 한 my_text 목록은 기억 장치 내에 유지된다는 점이 중요하다. 그로 인하여 각 yield에 대하여 값을 목록에 추가할 수 있게 된다. 코루틴을 실제로 사용하는 방법을 알아보도록 하자.

예제 4-43.

>>> ex = co_example("example1")
>>> ex.next()
Entering coroutine example1

이 코드에서는, 코루틴에 "example1"이라는 이름을 부여하였다. 코루틴은 어떠한 유형의 인자든지 받아들일 수 있으며 원하는 것은 무엇이든 할 수 있다. 우선 이것의 작동 원리부터 이해한 다음에 더 좋은 예를 들어보겠다. 그 뿐만 아니라, 이 코루틴을 서로 다른 이름의 여러 변수에 할당할 수 있는데, 그렇게 되면 각각은 고유한 코루틴 개체가 되어 독립적으로 동작할 수 있다. 코드의 다음 행은 함수에 대하여 next()를 호출한다. next()는 코루틴을 초기화하기 위하여 반드시 한번 호출되어야 한다. 이것이 완료되면, 함수는 비로소 값을 받아들일 준비가 된다.

예제 4-44.

>>> ex.send("test1")
['test1']
>>> ex.send("test2")
['test1', 'test2']
>>> ex.send("test3")
['test1', 'test2', 'test3']

위에서는 send() 메서드를 사용하여 코루틴 안으로 실제로 자료 값을 보낸다. 함수 자체에서, 우리가 보내는 텍스트는 (yield) 표현식이 있는 곳에 삽입된다. 코루틴은 우리의 JVM의 메모리가 부족하지 않는 한 계속 사용할 수 있다. 어찌됐든, 코루틴이 더 이상 필요하지 않을 때에는 close()하는 것이 최선의 방책이다. close() 호출은 코루틴이 쓰레기 수집이 되도록 한다.

예제 4-45.

>>> ex.close()
>>> ex.send("test1")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

함수가 닫힌 이후에 데이터를 추가로 보내면 StopIteration 오류가 발생한다. 코루틴은 여러 가지 상황에서 큰 도움이 될 수 있다. 앞의 예제에는 그리 많은 내용이 없지만, 코루틴을 적용한 훌륭한 애플리케이션이 여럿 있으며, 다음 절에서 보다 유용한 예를 살펴볼 것이다.

코루틴 장식

next() 메서드를 호출함으로써 코루틴을 초기화하는 것이 그리 어렵지는 않지만, 이 단계를 생략하는 것도 가능하다. 장식 함수를 코루틴에 적용함으로써, 자동으로 초기화를 수행한 다음 데이터 수신을 준비하도록 할 수 있다.

next()를 호출하도록 하기 위하여 코루틴에 적용할 수 있는 장식을 정의해보자.

예제 4-46.

def coroutine_next(f):
    def initialize(*args,**kwargs):
        coroutine = f(*args,**kwargs)
        coroutine.next()
        return coroutine
    return initialize

이제 코루틴 함수에 장식을 적용한 다음에 그것을 사용한다.

>>> @coroutine_next
... def co_example(name):
...     print 'Entering coroutine %s' % (name)
...     my_text = []
...     while True:
...         txt = (yield)
...         my_text.append(txt)
...         print my_text
...
>>> ex2 = co_example("example2")
Entering coroutine example2
>>> ex2.send("one")
['one']
>>> ex2.send("two")
['one', 'two']
>>> ex2.close()

이러한 일을 수행하기 위해 장식을 사용하는 것이 필수적이지는 않지만, 분명히 편리하다는 것을 알 수 있다. 문법적인 감초인 @ 구문을 사용하는 것이 내키지 않는다면, 다음과 같이 coroutine_next() 함수를 가지고 코루틴을 초기화할 수 있다.

예제 4-47.

co_example = coroutine_next(co_example)

코루틴 예제

이제 코루틴을 어떻게 사용하는지 이해하였으니, 보다 깊이있는 예제를 살펴보도록 하자. 이 예제를 검토하고 나면 그러한 기능이 얼마나 유용한지 이해할 수 있을 것으로 기대한다. 이 예제에서, 우리는 초기화 시에 파일의 이름을 코루틴에 전달한다. 그런 다음, 텍스트 문자열을 함수로 보내어 텍스트 파일(올바른 위치에 존재한다고 가정한다)을 열고, 주어진 단어에 대하여 일치하는 갯수를 구한다. 일치의 수에 대한 숫자 결과는 사용자에게 반환된다.

예제 4-48.

def search_file(filename):
    print 'Searching file %s' % (filename)
    my_file = open(filename, 'r')
    file_content = my_file.read()
    my_file.close()
    while True:
        search_text = (yield)
        search_result = file_content.count(search_text)
        print 'Number of matches: %d' % (search_result)

위의 코루틴은 주어진 파일을 열고, 그 내용을 읽은 다음, 주어진 send 호출과 일치하는 것을 찾아 그 수를 반환한다.

예제 4-49.

>>> search = search_file("example4_3.txt")
>>> search.next()
Searching file example4_3.txt
>>> search.send('python')
Number of matches: 0
>>> search.send('Jython')
Number of matches: 1
>>> search.send('the')
Number of matches: 4
>>> search.send('This')
Number of matches: 2
>>> search.close();

요약

이 장에서, 우리는 파이썬 언어에서의 함수의 사용을 다루었다. 함수의 여러 가지 용례가 있으며 여러 가지 상황에서 함수를 사용할 수 있는 기법을 배웠다. 함수는 파이썬의 일급 개체이며, 다른 개체와 마찬가지로 취급할 수 있다. 이 장에서는 먼저 함수를 정의하는 방법의 기초를 배웠다. 기본 사항에 대해 학습한 후에, 우리는 매개 변수를 사용하는 방법과 재귀 함수 호출을 학습하여 함수에 대한 지식을 쌓았다. 다양한 종류의 내장 함수가 사용 가능하다. 이 책의 부록 C에서 이러한 기본 함수의 목록을 찾을 수 있다. 어떤 내장 함수가 있는지 잘 알아두는 것이 좋다. 이미 만들어져 있을 것을 다시 만들 필요는 없기 때문이다. 또한 이번 장에서는 람다 표기법 등의 기능을 정의하는 몇 가지 다른 방법 뿐만 아니라, 장식, 생성기, 코루틴 등 함수를 정의하는 여러가지 다른 방법에 대하여 논의하였다. 이번 장을 통하여, 파이썬의 함수에 대하여, 그리고 함수를 어떻게 만들고 사용하는지에 대하여 잘 알게 되었을 것이다. 또한 함수에 적용할 수 있는 고급 기술 몇 가지를 잘 알 수 있을 것이다. 다음 장에서는, 자이썬의 입력과 출력, 그리고 파이썬 입출력의 기초를 배울 것이다. 이 책의 뒷부분에서, 객체지향 및 파이썬에서 클래스를 어떻게 사용하는지에 대하여 배우게 될 것이다.