80

Python Programming: Function

Embed Size (px)

Citation preview

Page 1: Python Programming: Function

Python Intermediate Programming

임찬식 ([email protected])

1

Page 2: Python Programming: Function

Python Intermediate Programming타입과 객체

함수와 함수형 프로그래밍

클래스와 객체지향 프로그래밍

데이터 구조

튜닝과 최적화

2

Page 3: Python Programming: Function

함수와 함수형 프로그래밍함수

유효 범위 규칙

클로저

장식자

생성기

코루틴

리스트 내포 (list comprehension)생성기 표현식

3

Page 4: Python Programming: Function

함수

함수는 def 문으로 정의

>>> def add(x, y):... return x + y...>>> add(10, 20)30

함수의 이름을 적고 바로 이어서 함수 인자들의 튜플을 나열해 호출

인수의 순서와 개수는 함수에서 정의한 것과 일치해야함

인수가 일치하지 않을 경우 TypeError

4

Page 5: Python Programming: Function

함수

함수 매개변수에 기본 인수 지정

>>> def power(x, y=2):... return x ** y...>>> power(2)4>>> power(3, 3)27

5

Page 6: Python Programming: Function

함수

기본 매개변수 값은 함수를 정의할 때 지정한 객체로 설정

>>> a = 10>>> def foo(x=a):... return x...>>> a = 5>>> foo()10

위에서 'a' 를 재할당하더라도 기본 값이 변경되지 않음

6

Page 7: Python Programming: Function

함수

변경 가능한 객체를 기본 값으로 지정할 경우 의도하지 않은 결과 발생 가능

>>> def foo(x, items=[]):... items.append(x)... return items...>>> foo(1)[1]>>> foo(2)[1, 2]>>> foo(3)[1, 2, 3]

7

Page 8: Python Programming: Function

함수

기본 인수가 이전 호출 때의 변경 사항을 담고 있음

이러한 문제를 피하기 위해 기본 값으로 None 을 지정하는 것을 권장

>>> def foo(x, items=None):... if items is None:... items = []...... items.append(x)... return items...>>> foo(1)[1]>>> foo(2)[2]>>> foo(3)[3]

8

Page 9: Python Programming: Function

함수

마지막 매개변수 이름 앞에 별표(*)를 추가해 여러 개의 매개변수 받는 것 가능

남아있는 모든 인수가 튜플로써 args 변수에 저장

튜플 args 를 매개변수인 것처럼 함수에 전달하려면 *args 를 사용

>>> def fprintf(file, fmt, *args):... file.write(fmt % args)...>>> import sys>>>>>> fprintf(sys.stdout, "%d %s %f\n", 42, 'Hello, World', 42 Hello, World 3.140000

9

Page 10: Python Programming: Function

함수

남아있는 모든 인수를 저장한 변수를 다른 함수에 인자로 전달

>>> def printf(fmt, *args):... fprintf(sys.stdout, fmt, *args)...>>> printf("%d %s %f\n", 42, 'Hello, World', 3.14)42 Hello, World 3.140000

10

Page 11: Python Programming: Function

함수

함수 인수를 매개변수 이름과 값을 이용해 지정

키워드 인수(keyword argument)

>>> def foo(w, x, y, z):... print(w, x, y, z)...>>>>>> foo(x=3, y=10, w='Hello', z=[1, 2, 3, 4])Hello 3 10 [1, 2, 3, 4]

키워드 인수를 사용할 때는 매개변수 순서가 중요하지 않음

기본 값이 지정된 것을 제외하고 모든 매개변수를 직접 지정해주어야 함

11

Page 12: Python Programming: Function

함수

위치 인수와 키워드 인수를 섞어 사용하는 것도 가능

모든 위치 인수를 먼저 지정해야함

모든 생략 가능한 인수의 값을 지정해야함

인수 값을 두 번 이상 설정하면 TypeError

>>> foo('Hello', 3, z=[1, 2, 3, 4], y=42)Hello 3 42 [1, 2, 3, 4]>>> foo(3, 42, w='Hello', z=[1, 2, 3, 4])Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: foo() got multiple values for argument 'w'

12

Page 13: Python Programming: Function

함수

마지막 인수 이름이 **로 시작할 경우 어떤 매개변수 이름과도 일치하지않는모든 추가 키워드 인수들이 사전으로 구성되어 전달

>>> def make_table(data, **params):... fgcolor = params.pop("fgcolor", "black")... bgcolor = params.pop("bgcolor", "white")... width = params.pop("width", None)... height = params.pop("height", None)... if params:... raise TypeError( "Unsupported %s" % list(params))... print(data, fgcolor, bgcolor, width, height)...>>>>>> make_table([1, 2, 3, 4], width=640, height=480)[1, 2, 3, 4] black white 640 480>>> make_table([1, 2, 3, 4], font='Arial')Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 8, in make_tableTypeError: Unsupported options ['font']

13

Page 14: Python Programming: Function

함수

** 매개변수가 가장 나중에 나오는 경우에 한 해서 추가 키워드 인수들이가변 길이 인수 목록과 함께 나타날 수 있음

키워드 인수는 **kwargs 문법을 사용해 다른 함수로 전달 가능

>>> def foo(*args, **kwargs):... print(args, kwargs)...>>>>>> def call_foo(*args, **kwargs):... foo(*args, **kwargs)...>>> call_foo(10, 20, x=3.14, y=42)(10, 20) {'x': 3.14, 'y': 42}

14

Page 15: Python Programming: Function

매개변수 전달과 반환 값

함수 매개변수는 단순히 전달된 입력 객체를 참조하는 이름일뿐

변경 불가능한 값: 값에 의한 전달

변경 가능한 객체: 참조에 의한 전달

변경 가능한 객체가 함수에 전달되어 그 값이 변경되면 원래 객체 값에 Ý향

>>> a = [1, 2, 3, 4, 5]>>> def square(items):... for i, x in enumerate(items):... items[i] = x * x...>>> square(a)>>> a[1, 4, 9, 16, 25]

15

Page 16: Python Programming: Function

매개변수 전달과 반환 값

입력으로 넘어온 값을 변경하거나 프로그램의 다른 부분을 변경하는 함수는부작용(side effect) 을 가진다고 하고 이는 프로그램이 커질거나복잡해질수록 까다로운 문제를 발생시키는 원인이 될 수 있음

함수 호출이 어떤 형태로든 외부에 변경을 가할 경우에는 함수 이름이나주석을 통해 어디에 Ý향을 주는 지 명시하는 것을 권장

쓰레드나 병행 프로그래밍이 필요한 곳에서 부작용을 가지는 함수를사용하는 것은 매우 까다롭고 주의를 필요로함

16

Page 17: Python Programming: Function

매개변수 전달과 반환 값

반환할 값을 지정하지 않거나 return 문을 생략하면 None 객체가 반환여러 값을 반환하려면 튜플에 넣어서 반환

>>> def factor(a):... d = 2... while d <= (a / 2):... if int(a / d) * d == a:... return a / d, d... d += 1... return a, 1...>>> factor(16)(8.0, 2)>>> factor(37)(37, 1)>>> x, y = factor(39)>>> x, y(13.0, 3)

17

Page 18: Python Programming: Function

유효 범위 규칙

함수가 실행될 때마다 새로운 지역 네임 스페이스 생성

매개변수 이름과 함수에서 할당된 변수 이름을 담은 내부 실행 환경

변수 이름을 해석할 때 순서

지역 네임 스페이스 검색 (함수)

전역 네임 스페이스 검색 (함수가 정의된 모듈)

내장 네임 스페이스 검색

아무 곳에서도 찾을 수 없으면 NameError

18

Page 19: Python Programming: Function

유효 범위 규칙

함수에서 전역 네임 스페이스 접근

>>> a = 42>>> def foo():... a = 13...>>>... foo()>>> print(a)42

위 코드에서 foo() 함수는 전역 변수 a 를 변경할 수 없음

19

Page 20: Python Programming: Function

유효 범위 규칙

global 선언을 이용한 전역 네임 스페이스 접근

>>> a = 42>>> b = 37>>> def foo():... global a... a = 13... b = 0...>>>>>> foo()>>> print(a, b)13 37

foo() 함수에서 함수 밖에서 선언된 a 변수 값을 변경

20

Page 21: Python Programming: Function

유효 범위 규칙

중첩 함수 선언 가능

>>> def countdown(start):... n = start... def display():... print("T-minus %d" % n)...... while n > 0:... display()... n -= 1...>>>>>> countdown(5)T-minus 5T-minus 4T-minus 3T-minus 2T-minus 1

21

Page 22: Python Programming: Function

유효 범위 규칙

중첩 함수에서 값을 재할당 할 수 있는 범위는 중첩 함수 내부와 전역만 가능

중첩 함수 내부 지역 변수

global 을 이용한 전역 변수

>>> def countdown(start):... n = start... def display():... print("T-minus %d" % n)... def decrement():... n -= 1 # 바깥 함수에 정의된 n 재정의 불가... while n > 0:... display()... decrement()...>>> countdown(5)T-minus 5Traceback (most recent call last): ...UnboundLocalError: local variable 'n' referenced before assignment

22

Page 23: Python Programming: Function

유효 범위 규칙

Python 2 에서는 변경하고자 하는 값을 리스트나 사전에 넣어서 사용 가능Python 3 에서는 nonlocal 선언을 이용해 사용 가능

>>> def countdown(start):... n = start... def display():... print("T-minus %d" % n)... def decrement():... nonlocal n... n -= 1... while n > 0:... display()... decrement()...>>> countdown(5)T-minus 5T-minus 4T-minus 3T-minus 2T-minus 1

23

Page 24: Python Programming: Function

유효 범위 규칙

지역 변수에 값을 할당하기 전에 사용하면 UnboundLocalError

foo() 함수 안에서 변수 i 는 지역 변수로 선언되었음

하지만 i = i + 1 에서 i 에 값이 할당되기 전에 읽으려 시도

변수는 함수를 정의하는 순간에 지역 또는 전역인지 여부가 결정되고중간에 유효 범위가 변경되는 일은 없음

>>> i = 0>>> def foo():... i = i + 1... print(i)...>>> foo()Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in fooUnboundLocalError: local variable 'i' referenced before assignment

24

Page 25: Python Programming: Function

객체와 클로저로서 함수

함수는 1급 객체이므로 함수 자체를 다른 함수 인자로 전달하거나자료 구조 안에 저장하는 것, 함수 결과로서 반환하는 것도 모두 가능

# foo.pydef callf(func): return func()

함수를 인자로 받아 호출하는 예

>>> import foo>>>>>> def helloworld():... return 'Hello, World!'...>>> foo.callf(helloworld)'Hello, World!'

25

Page 26: Python Programming: Function

객체와 클로저로서 함수

함수를 데이터로 취급할 때는 함수가 정의된 곳 주변 환경 정보를 같이 저장

# foo.pyx = 42def callf(func): return func()

주변 변수를 참조하도록 변경

>>> import foo>>> foo.x42>>> x = 37>>> def helloworld():... return 'Hello, World!. x is %d' % x...>>> foo.callf(helloworld)'Hello, World!. x is 37'

26

Page 27: Python Programming: Function

객체와 클로저로서 함수

앞의 예에서 변수 x 는 foo.py 에 정의되어 있고, helloworld() 함수가호출된 곳도 foo.py 이지만 출력된 값은 그 안에 정의된 값(foo.x)이 아님

함수를 구성하는 문장과 실행 환경을 함께 묶은 것이 클로저(closure)

함수는 자신이 정의된 전역 네임스페이스를 가리키는 globals 속성을 가짐

>>> helloworld.__globals__{'__name__': '__main__', '__doc__': None, '__package__': None, ...'x': 37, 'helloworld': <function helloworld at 0x10230cf28

27

Page 28: Python Programming: Function

객체와 클로저로서 함수

클로저와 중첩 함수는게으른 평가(Lazy evaluation) 또는 지연 평가(Delayed evaluation) 라는개념에 기초하여 코드를 작성할 때 유용

>>> from urllib.request import urlopen>>> # from urllib import urlopen # Python 2...>>> def page(url):... def get():... return urlopen(url).read()... return get...

위에 코드에서 page() 함수는 웹 페이지 내용을 가져오는 get() 함수를생성해 반환하는 일 외에는 아무런 일도 수행하지 않음

get() 함수 실행은 나중에 get() 이 평가되는 순간까지 미뤄짐28

Page 29: Python Programming: Function

객체와 클로저로서 함수

두 변수 python 과 jython 은 실제로 get() 함수의 서로 다른 버전page() 함수는 더 이상 실행되지 않지만 get() 함수가 실행할 때정의된 바깥쪽 변수의 값을 가져와 사용

get() 함수가 실제로 실행할 때 원래 page() 함수에 제공된 url 값으로urlopen(url) 을 호출

>>> python = page("http://www.python.org")>>> jython = page("http://www.jython.org")>>> python<function page.<locals>.get at 0x102b842f0>>>> jython<function page.<locals>.get at 0x102b84378>>>> py_data = python() # http://www.python.org 를 얻어옴>>> jy_data = jython() # http://www.jython.org 를 얻어옴

29

Page 30: Python Programming: Function

객체와 클로저로서 함수

클로저를 살펴보면 어떤 변수가 함께 묶여있는지 확인 가능

>>> python.__closure__(<cell at 0x102309528: str object at 0x102b7adf8>,)>>> python.__closure__[0].cell_contents'http://www.python.org'>>> jython.__closure__[0].cell_contents'http://www.jython.org'

30

Page 31: Python Programming: Function

객체와 클로저로서 함수

클로저는 일련의 함수 호출 사이에 상태 정보를 보존하는 데 효율적

>>> def countdown(n):... def next():... nonlocal n... r = n... n -= 1... return r... return next...>>> next = countdown(3)>>> while True:... v = next()... print(v)... if not v:... break...3210

31

Page 32: Python Programming: Function

객체와 클로저로서 함수

클래스를 이용한 countdown 구현

>>> class Countdown(object):... def __init__(self, n):... self.n = n... def next(self):... r = self.n... self.n -= 1... return r...>>>>>> c = Countdown(3)>>> while True:... v = c.next()... print(v)... if not v:... break...3210 32

Page 33: Python Programming: Function

장식자

장식자(decorator)는 다른 함수나 클래스를 포장하는 것이 주 목적

포장하려는 객체의 작동 방식을 변경하거나 향상시키는 작업 수행

특수 문자 @ 를 사용하여 표시

def square(x): return x * x

square = trace(square)

위의 코드는 장식자를 이용해 간단히 작성 가능

@tracedef square(x): return x * x

33

Page 34: Python Programming: Function

장식자

장식자는 반드시 함수나 클래스 정의 바로 앞에 별개의 줄로 적어야 함

@foo@bar@somethingdef func(x): pass

이 경우 장식자는 나열된 순서�로 적용

def func(x): passfunc = foo(bar(something(func)))

34

Page 35: Python Programming: Function

장식자

장식자에 인수를 넘기는 것도 가능

@eventhandler("BUTTON")def handle_button(msg): ...

@eventhandler("RESET")def handle_reset(msg): ...

이 경우에는 아래 코드처럼 동작

def handle_button(msg): ...t = eventhandler("BUTTON") # 제공된 인자로 장식자 호출handle_button = t(handle_button) # 호출 결과 장식자로 원래 함수 호출

35

Page 36: Python Programming: Function

장식자

장식자 함수는 @ 지정자로 제공한 인수만을 받아 주어진 함수를인수로 하여 호출될 새로운 함수 반환

event_handler = {}def eventhandler(event): def register_function(f): event_handler[event] = f return f return register_function

36

Page 37: Python Programming: Function

생성기와 yield

함수에서 yield 키워드를 사용해 생성기(generator) 객체를 정의

생성기는 반복에서 사용할 값들을 생성하는 함수

>>> def countdown(n):... print("Counting down from %d" % n)... while n > 0:... yield n... n -= 1... return...>>> c = countdown(3)>>>

위 함수를 호출하면 아무 코드도 실행되지 않고 생성기 객체를 반환

37

Page 38: Python Programming: Function

생성기와 yield

생성기를 반환하는 함수에서 return 문을 사용하는 경우

3.3 이전 버전에서는 문법 오류

3.3 버전부터는 무시되지 않고 return 값을 반환하고 종료return 문 뒤에 있는 yield 는 무시

참고할 링크https://www.python.org/dev/peps/pep‑0380/http://stackoverflow.com/questions/16780002/return‑in‑generator‑together‑with‑yield‑in‑python‑3‑3

38

Page 39: Python Programming: Function

생성기와 yield

>>> c = countdown(3)>>> type(c)<class 'generator'>

반환된 생성기 객체를 이용해 실제 함수 실행Python 3 에서는 __next__(), Python 2 에서는 next() 호출

>>> c.__next__()Counting down from 33>>> c.__next__()2>>> c.__next__()1>>> c.__next__()Traceback (most recent call last): File "<stdin>", line 1, in <module>StopIteration

39

Page 40: Python Programming: Function

생성기와 yield

생성기 실행 순서

__next()__ 를 호출하면 생성기 함수는 yield 문까지 실행

yield 문은 결과를 생성해 반환하고 __next()__ 호출 전까지 실행 중단

함수 실행은 yield 바로 다음 문장에서 재개

보통은 생성기를 직접 호출하기보다는 순서열을 소비하는 for, sum() 혹은기타 연산에서 __next()__ 호출이 사용됨

>>> for n in countdown(3):... print(n)...Counting down from 3321>>> sum(countdown(3))Counting down from 36

40

Page 41: Python Programming: Function

생성기와 yield

생성기 함수를 부분적으로만 실행하는 경우에는 close() 메서드를 호출해명시적으로 생성기 객체에 종료를 알려주는 것이 가능

>>> c = countdown(10)>>> c.__next__()Counting down from 1010>>> c.__next__()9>>> c.close()>>> c.__next__()Traceback (most recent call last): File "<stdin>", line 1, in <module>StopIteration

41

Page 42: Python Programming: Function

생성기와 yield

생성기 함수안에서는 yield 문에서 GeneratorExit 예외가 발생하는 것으로외부에서 close() 함수 호출한 것을 알 수 있음

>>> def countdown(n):... print("Counting down from %d" % n)... try:... while n > 0:... yield n... n -= 1... except GeneratorExit:... print("Only made it to %d" % n)...>>> c = countdown(10)>>> c.__next__()Counting down from 1010>>> c.__next__()9>>> c.close()Only made it to 9

42

Page 43: Python Programming: Function

코루틴과 yield 표현식

함수 안에서 yield 문을 �입 연산자 오른쪽에 두고 값을 받아오도록 하는 것이가능

>>> def receiver():... print("Ready to receive")... while True:... n = yield... print("Got %s" % n)...

위의 코드처럼 yield 를 사용하는 함수를 코루틴(coroutine) 이라고 함

함수 외부에서 보내준 값에 답하여 실행

동작 방식은 생성기와 매우 유사

43

Page 44: Python Programming: Function

코루틴과 yield 표현식

>>> r = receiver()>>> r.__next__() # Python 2 에서는 r.next()Ready to receive>>> r.send(1)Got 1>>> r.send(2)Got 2>>> r.send("Hello, World!")Got Hello, World!

44

Page 45: Python Programming: Function

코루틴과 yield 표현식

1. 제일 처음 __next()__ 호출을 통해 코루틴 첫 번째 yield 표현식까지 실행

2. yield 문을 만나면 실행을 중단하고 연관된 생성기 객체 r 의send() 메서드를 통해 값이 보내지기를 기다림

3. send() 를 통해 전달된 값은 코루틴 안의 yield 표현식에 의해 반환

4. 값을 받아 코루틴은 다음 yield 문까지 실행

45

Page 46: Python Programming: Function

코루틴과 yield 표현식

코루틴에 �해서 __next()__ (Python 2: next())를 먼저 호출해야 한다는 점을잊는 실수를 방지하기 위해 장식자로 코루틴을 감싸는 방법을 권장

>>> def coroutine(func):... def start(*args, **kwargs):... g = func(*args, **kwargs)... g.__next__()... return g... return start...

46

Page 47: Python Programming: Function

코루틴과 yield 표현식

앞에서 만든 장식자를 이용해 코루틴 작성 및 실행

>>> @coroutine... def receiver():... print("Ready to receive")... while True:... n = yield... print("Got %s" % n)...>>> r = receiver()Ready to receive>>> r.send("Hello, World!")Got Hello, World!>>> r.send(1)Got 1

47

Page 48: Python Programming: Function

코루틴과 yield 표현식

코루틴은 직접 종료시키거나 스스로 종료하는 경우가 아니라면 보통 계속 실행입력 값 스트림을 닫기 위해서는 close() 메서드 호출

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

코루틴이 닫히면 코루틴에 추가 값을 보낼 경우StopIteration 예외 발생

48

Page 49: Python Programming: Function

코루틴과 yield 표현식

코루틴 생성기 객체에 close() 를 호출하면 코루틴 안에서는GeneratorExit 예외 발생

>>> @coroutine... def receiver():... print("Ready to receive")... try:... while True:... n = yield... print("Got %s" % n)... except GeneratorExit:... print("Receiver done")...>>> r = receiver()Ready to receive>>> r.send("Hello, World!")Got Hello, World!>>> r.close()Receiver done

49

Page 50: Python Programming: Function

코루틴과 yield 표현식

yield 표현식에 값을 줄 경우 코루틴은 값을 받는 동시에 반환하는 것이 가능

>>> @coroutine... def line_splitter(delimiter=None):... print("Ready to split")... result = None... while True:... line = yield result... result = line.split(delimiter)...

앞서 사용한 것과 같은 방식으로 코루틴을 호출해 사용

50

Page 51: Python Programming: Function

코루틴과 yield 표현식

코루틴에 send() 를 통해 값을 넘기고 결과 값을 받는 것이 가능

>>> s = line_splitter(",")Ready to split>>> s.send("A,B,C")['A', 'B', 'C']>>> s.send("100,200,300")['100', '200', '300']

1. 첫 번째 __next()__ 호출을 하면 코루틴이 yield result 까지 진행이 때 result 기본 값인 None 을 반환

2. 이어지는 send() 호출에서는 받은 값을 line 에 저장하고분할해서 result 에 저장

3. send() 의 반환 값은 send() 에 전달된 값을 받는 역할을 수행한yield 표현식이 아닌 그 다음 yield 표현식의 값

51

Page 52: Python Programming: Function

생성기와 코루틴 사용

생성기와 코루틴은 시스템, 네트워크, 분산 처리 시스템등 특정한 종류의프로그래밍을 수행할 때 �단히 효과적으로 사용 가능

예를 들어 생성기 함수는 유닉스 셸에서 파이프를 사용하는 것과

유사한 처리 파이프라인을 만들고자 할 때 유용하게 사용할 수 있음

코루틴은 데이터 흐름에 따라 처리하는 프로그램을 작성할 때효율적으로 사용 가능

52

Page 53: Python Programming: Function

생성기와 코루틴 사용

import osimport fnmatchdef find_files(topdir, pattern): for path, dirname, filelist in os.walk(topdir): for name in filelist: if fnmatch.fnmatch(name, pattern): yield os.path.join(path, name)

def opener(filenames): for name in filenames: f = open(name) yield f

def cat(filelist): for f in filelist: for line in f: yield line

def grep(pattern, lines): for line in lines: if pattern in line: yield line

53

Page 54: Python Programming: Function

생성기와 코루틴 사용

생성기를 이용한 함수를 조합해 처리 파이프라인 생성 예제

>>> py_files = find_files(".", "*.py")>>> files = opener(py_files)>>> lines = cat(files)>>> print_lines = grep("print", lines)>>> for line in print_lines ... print('Wrote setup.py') ... print(HELP_TEXT) .... print('aborted!') ....

54

Page 55: Python Programming: Function

생성기와 코루틴 사용

1. 현재 디렉토리부터 시작해 하위 디렉토리 안에 있는 "*.py" 파일 탐색

2. 발견한 파일을 열어 파일 객체 생성

3. 파일에서 모든 줄을 가져와 부분 문자열을 찾는 필터에 전달

4. 필터에서는 "print" 문자열을 검색해 전달

5. 마지막 for 문에 의해 모든 전체 과정이 실제로 진행

6. for 루프는 각 반복마다 파이프라인을 통해 새로운 값을 가져와 소비

이러한 구현 방식은 임시 리스트나 기타 �규모 자료구조를 생성하지 않고모든 처리를 진행하므로 메모리 사용 측면에서 매우 효율적

55

Page 56: Python Programming: Function

생성기와 코루틴 사용

@coroutinedef find_files(target): while True: topdir, pattern = yield for path, dirname, filelist in os.walk(topdir): for name in filelist: if fnmatch.fnmatch(name, pattern): target.send(os.path.join(path, name))

@coroutine @coroutinedef opener(target): def cat(target): while True: while True: name = yield f = yield f = open(name) for line in f: target.send(f) target.send(line)

@coroutine @coroutinedef grep(pattern, target): def printer(): while True: while True: line = yield line = yield if pattern in line: sys.stdout.write(line) target.send(line)

56

Page 57: Python Programming: Function

생성기와 코루틴 사용

>>> finder = find_files(opener(cat( grep("print", printer()))))>>> finder.send((".", "*.py")) ... print(sum_for_list([12, 15])) ...

1. 첫 번째 코루틴 find_files() 에 데이터를 Â어넣으면서 시작

2. 각 코루틴은 인수 target 으로 지정된 다른 코루틴으로 데이터를 전송

3. 필요할 때마다 send() 를 호출해 파이프라인 사용

4. 파이프라인이 close() 를 직접 호출하기 전까지는 계속 사용 가능

데이터가 코루틴에 전달되기 때문에 프로그램 구성 요소 사이에통신을 하기 위해서 필요한 메시지 큐 등의 메시지 전달 기능을 구현하기 편함

57

Page 58: Python Programming: Function

리스트 내포

리스트 각 아이템에 함수를 적용하고 그 결과로 새로운 리스트를생성하는 연산을 자주 사용함

>>> nums = [1, 2, 3, 4, 5]>>> squares = []>>> for n in nums:... squares.append(n * n)...>>> squares[1, 4, 9, 16, 25]

이러한 방식을 이용한 연산을 매우 자주하기 때문에리스트 내포(list comprehension)라고 부르는 연산자로 사용

58

Page 59: Python Programming: Function

리스트 내포

리스트 내포를 이용한 새로운 리스트 생성 예

>>> nums = [1, 2, 3, 4, 5]>>> squares = [n * n for n in nums]>>> squares[1, 4, 9, 16, 25]

59

Page 60: Python Programming: Function

리스트 내포

리스트 내포의 일반적인 문법

[표현식 for 항목1 in 반복가능객체1 if 조건1 for 항목2 in 반복가능객체2 if 조건2 ... for 항목n in 반복가능객체n if 조건n ]

이 문법은 �략 다음 코드로 변환 가능

s = []for 항목1 in 반복가능객체1: if 조건1: for 항목2 in 반복가능객체2: if 조건2: ... for 항목n in 반복가능객체n: if 조건n: s.append(표현식)

60

Page 61: Python Programming: Function

리스트 내포

>>> a = [-3, 5, 2, -7, 10, 4]>>> b = 'abcd'>>> c = [2 * i for i in a]>>> c[-6, 10, 4, -14, 20, 8]>>> d = [i for i in a if i >= 0]>>> d[5, 2, 10, 4]>>> e = [(x, y) for x in a... for y in b... if x > 0 ]>>> e[(5, 'a'), (5, 'b'), ... , (4, 'b'), (4, 'c'), (4, 'd')]

61

Page 62: Python Programming: Function

리스트 내포

>>> f = [(1, 2), (3, 4), (5, 6)]>>> g = [math.sqrt(x * x + y * y) for x, y in f]>>> g[2.23606797749979, 5.0, 7.810249675906654]

리스트 내포 안에서 정의한 반복 변수 유효 범위

Python 2 에서는 리스트 생성 연산 수행 후에도 남아있음

Python 3 에서는 내부 변수로만 사용됨

62

Page 63: Python Programming: Function

생성기 표현식

생성기 표현식(generator expression)은 리스트 내포와 동일한 계산 수행

결과를 반복적으로 생성하는 객체

문법은 괄호 사용

실제로 리스트를 생성하거나 표현식을 바로 평가하지 않음

반복을 통해 필요할 때 값을 생성하는 생성기 객체 반환

생성기 표현식 문법

(표현식 for 항목1 in 반복가능객체1 if 조건1 for 항목2 in 반복가능객체2 if 조건2 ... for 항목n in 반복가능객체n if 조건n )

63

Page 64: Python Programming: Function

생성기 표현식

>>> a = [1, 2, 3, 4]>>> b = (i * i for i in a)>>> b<generator object <genexpr> at 0x10f235678>>>> b.__next__()1>>> b.__next__()4>>> b.__next__()9

생성기 표현식와 리스트 내포에 �한 연산 결과는 큰 차이가 없지만,특정 응용 프로그램 상황에 따라 성능과 메모리 효율을 증가시킬 수 있음

64

Page 65: Python Programming: Function

생성기 표현식

파일에서 줄을 추출해 공백을 제거하고 주석을 추출하는 부분은실제로 전체 파일을 읽지 않음

실제 파일 내용은 for 루프에서 반복을 시작할 때 줄 단위로 읽어사용할 뿐 전체 파일 내용을 메모리에 올리는 일은 없음

>>> f = open('python_sample.py')>>> lines = (t.strip() for t in f)>>> comments = (t for t in lines if len(t) > 0 and t[0] == >>> for c in comments:... print(c) ...# db.app = flask_app# db.init_app(flask_app)# db.create_all() ...

65

Page 66: Python Programming: Function

선언형 프로그래밍

데이터에 �해 직접 반복을 수행하는 프로그램을 작성하는 �신에,모든 데이터에 한 번에 적용되는 계산으로 프로그램을 구조화

예를 들어 다음 파일을 읽어서 두 번째 열과 세 번째 열을 곱하고전부 더하는 계산 프로그램을 작성할 경우

AA 100 32.20IBM 50 91.10CAT 150 83.44MSFT 200 51.23GE 95 40.37

66

Page 67: Python Programming: Function

선언형 프로그래밍

파일을 읽어 한 줄씩 반복하는 프로그래밍적 요소에 �해서는 신경 쓰지않고모든 데이터를 어떻게 계산할지에 �한 선언적인 요소만 고민하여 코드 작성

>>> lines = open("portfolio.txt")>>> fields = (line.split() for line in lines)>>> print(sum(float(f[1]) * float(f[2]) for f in fields))34372.15

67

Page 68: Python Programming: Function

lambda 연산자

lambda 문을 사용해 표현식 형태로 된 익명 함수 작성 가능

lambda args : expression

args: 콤마로 분리된 인수 목록

expression: 인수와 관련된 표현식

68

Page 69: Python Programming: Function

lambda 연산자

lambda 를 이용한 예

>>> a = lambda x, y: x + y>>> r = a(2, 3)>>> r5

콜백 함수로 사용하는 예

>>> names = ["Keith Athens", "Ilana Weidenbach", ... "Caron Tseng", "Thad Hamill"]>>> names.sort(key=lambda n: n.lower())>>> names['Caron Tseng', 'Ilana Weidenbach', 'Keith Athens', 'Thad Hamill'

69

Page 70: Python Programming: Function

재귀

재귀 함수는 호출 깊이 제한을 가지며 기본 값은 1000

>>> import sys>>> sys.getrecursionlimit()1000

값을 변경할 수 있지만 운Ý체제에서 설정된스택 크기 제한을 넘는 것은 불가능

고리 재귀(tail‑recursion) 최적화를 수행하지 않음

70

Page 71: Python Programming: Function

재귀

재귀를 이용한 factorial() 함수 구현 예

>>> def factorial(n):... if n <= 1:... return 1... else:... return n * factorial(n - 1)...>>> factorial(10)3628800

71

Page 72: Python Programming: Function

문서화 문자열

함수의 첫 번째 문장은 주로 함수를 설명하는 문서화 문자열인 경우가 많음

문서화 문자열은 함수의 __doc__ 속성에 저장

IDE 에서 이를 활용해 도움말 표시

>>> def factorial(n):... """Computes n factorial. For example:... >>> factorial(6)... 120... >>>... """... if n <= 1:... return 1... else:... return n * factorial(n - 1)...>>> factorial.__doc__'Computes n factorial. For example:\n\n >>> factorial(6)\n 120\n >>>\n '

72

Page 73: Python Programming: Function

문서화 문자열

>>> help(factorial)

Help on function factorial in module __main__:

factorial(n) Computes n factorial. For example: >>> factorial(6) 120 >>>(END)

73

Page 74: Python Programming: Function

문서화 문자열

장식자를 사용할 경우에는 장식자 래퍼로 인해 문서화 문자열을제�로 가져오지 못하는 문제점 발생

>>> def wrap(func):... def call(*args, **kwargs):... return func(*args, **kwargs)... return call...>>> @wrap... def factorial(n):... """Computes n factorial. For example: ... """...>>> help(factorial)

Help on function call in module __main__:

call(*args, **kwargs)(END)

74

Page 75: Python Programming: Function

문서화 문자열

장식자 함수에서 함수 이름과 문서화 문자열을 가져오도록 구현하는 것 필요

>>> def wrap(func):... def call(*args, **kwargs):... return func(*args, **kwargs)... call.__doc__ = func.__doc__... call.__name__ = func.__name__... return call...>>> help(factorial)>>> Help on function factorial in module __main__:

factorial(*args, **kwargs) Computes n factorial. For example: >>> factorial(6) 120 >>>(END)

75

Page 76: Python Programming: Function

문서화 문자열

장식자 함수에서 함수 이름과 문서화 문자열을 복사하는 패턴이 반복되므로이를 위해 functools 모듈로 wraps 함수 제공

>>> from functools import wraps>>> def wrap(func):... @wraps(func)... def call(*args, **kwargs):... return call(*args, **kwargs)... return call

76

Page 77: Python Programming: Function

함수 속성

함수는 그 자체가 임의의 속성을 가지는 것이 가능

함수 속성은 함수의 __dict__ 속성을 통해 접근 가능

함수 객체에 추가 정보를 저장하려는 프레임워크등 특수한 경우에 사용

>>> def foo():... pass...>>> foo.secure = 1>>> foo.private = 1>>> foo.__dict__{'secure': 1, 'private': 1}

77

Page 78: Python Programming: Function

eval(), exec(), compile()

eval(str [,globals [,locals]])함수는 표현식을 담은 문자열을 실행하고 결과를 반환

>>> a = eval("2 * math.pi * 8")>>> a50.26548245743669

exec(str [, globals [, locals]])함수는 임의의 파이썬 코드를 담은 문자열을 실행

>>> a = [3, 5, 7, 9]>>> exec("for i in a: print(i)")3579

78

Page 79: Python Programming: Function

eval(), exec(), compile()

eval(), exec() 두 함수 모두 호출자의 네임스페이스 안에서 코드를 실행함수에 전역, 지역 네임스페이스 역할을 하는 매핑 객체를 넘겨 실행 가능

>>> globals = {... 'x': 37,... 'y': 42,... 'birds': ['Parrot', 'Swallow', 'Alabatross']... }>>> locals = {}>>> a = eval("x + 2 * y", globals, locals)>>> a121>>> exec("for i in birds: print(i)", globals, locals)ParrotSwallowAlabatross

79

Page 80: Python Programming: Function

eval(), exec(), compile()

문자열을 eval(), exec() 함수로 전달하면 파서는 바이트코드로 컴파일컴파일하는 시간을 줄이기 위해서 미리 생성해둔 바이트코드를 재사용

compile(str, filename, kind)

str: 컴파일할 코드

filename: 문자열이 정의된 파일

kind: 컴파일될 코드 종류single: 단일 문장

exec: 여러 문장

eval: 표현식

>>> c = compile("x + 2 * y", "", "eval")>>> eval(c, globals, locals)121

80