python programming: function

Post on 11-Apr-2017

55 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Python Intermediate Programming

임찬식 (chanshik@gmail.com)

1

Python Intermediate Programming타입과 객체

함수와 함수형 프로그래밍

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

데이터 구조

튜닝과 최적화

2

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

유효 범위 규칙

클로저

장식자

생성기

코루틴

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

3

함수

함수는 def 문으로 정의

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

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

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

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

4

함수

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

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

5

함수

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

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

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

6

함수

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

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

7

함수

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

이러한 문제를 피하기 위해 기본 값으로 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

함수

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

남아있는 모든 인수가 튜플로써 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

함수

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

>>> 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

함수

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

키워드 인수(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

함수

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

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

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

인수 값을 두 번 이상 설정하면 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

함수

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

>>> 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

함수

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

키워드 인수는 **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

매개변수 전달과 반환 값

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

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

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

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

>>> 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

매개변수 전달과 반환 값

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

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

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

16

매개변수 전달과 반환 값

반환할 값을 지정하지 않거나 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

유효 범위 규칙

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

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

변수 이름을 해석할 때 순서

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

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

내장 네임 스페이스 검색

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

18

유효 범위 규칙

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

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

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

19

유효 범위 규칙

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

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

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

20

유효 범위 규칙

중첩 함수 선언 가능

>>> 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

유효 범위 규칙

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

중첩 함수 내부 지역 변수

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

유효 범위 규칙

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

유효 범위 규칙

지역 변수에 값을 할당하기 전에 사용하면 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

객체와 클로저로서 함수

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

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

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

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

25

객체와 클로저로서 함수

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

# 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

객체와 클로저로서 함수

앞의 예에서 변수 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

객체와 클로저로서 함수

클로저와 중첩 함수는게으른 평가(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

객체와 클로저로서 함수

두 변수 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

객체와 클로저로서 함수

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

>>> 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

객체와 클로저로서 함수

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

>>> 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

객체와 클로저로서 함수

클래스를 이용한 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

장식자

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

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

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

def square(x): return x * x

square = trace(square)

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

@tracedef square(x): return x * x

33

장식자

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

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

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

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

34

장식자

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

@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

장식자

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

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

36

생성기와 yield

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

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

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

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

37

생성기와 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

생성기와 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

생성기와 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

생성기와 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

생성기와 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

코루틴과 yield 표현식

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

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

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

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

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

43

코루틴과 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

코루틴과 yield 표현식

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

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

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

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

45

코루틴과 yield 표현식

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

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

46

코루틴과 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

코루틴과 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

코루틴과 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

코루틴과 yield 표현식

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

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

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

50

코루틴과 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

생성기와 코루틴 사용

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

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

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

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

52

생성기와 코루틴 사용

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

생성기와 코루틴 사용

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

>>> 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

생성기와 코루틴 사용

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

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

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

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

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

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

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

55

생성기와 코루틴 사용

@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

생성기와 코루틴 사용

>>> 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

리스트 내포

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

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

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

58

리스트 내포

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

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

59

리스트 내포

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

[표현식 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

리스트 내포

>>> 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

리스트 내포

>>> 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

생성기 표현식

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

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

문법은 괄호 사용

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

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

생성기 표현식 문법

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

63

생성기 표현식

>>> 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

생성기 표현식

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

실제 파일 내용은 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

선언형 프로그래밍

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

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

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

66

선언형 프로그래밍

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

>>> 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

lambda 연산자

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

lambda args : expression

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

expression: 인수와 관련된 표현식

68

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

재귀

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

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

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

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

70

재귀

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

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

71

문서화 문자열

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

문서화 문자열은 함수의 __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

문서화 문자열

>>> help(factorial)

Help on function factorial in module __main__:

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

73

문서화 문자열

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

>>> 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

문서화 문자열

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

>>> 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

문서화 문자열

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

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

76

함수 속성

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

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

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

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

77

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

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

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

top related