발로 하는 파이썬 세미나 안녕하세요. 저는 발로 하는 파이썬 세미나를 발표할…
강성훈 …강성훈입니다. 아는 사람은 알겠지만 석사 입학해서 잉여롭게 지내고 있습니다. 자세한 내용은 제 홈페이지를 참고하시고요. [잠시 쉬고] 제가 귀찮아서 슬라이드를 대강 대충 만들었으니까 앞으로 요런 것들만 잔뜩 나올 거에요. 저는 정식으로 발표하는 거 정말 못 하거든요. 뭐 좀 양해해 주심 고맙겠습니다.
http://j.mp/2010sparcspython
시작하기 전에: 오늘은 파이썬이라는 것을 배울 것입니다. 으레 그렇지만 뭔가 배우기 전에 한 번쯤 생각해 봐야 할 문제가 있죠.
왜? 왜 파이썬을 배우나요? 아니 이거 먼저 물어 봅시다. [웃으면서] 왜 이 세미나에 찾아 오셨나요? [답변을 대비해 조금 기다린다] 제 기억이 맞다면, 이게 방학 프로젝트 대비 교육 세미나라면서요. 근데 방학 프로젝트를 왜 파이썬으로 해야 하나? 라는 생각을 할 수도 있죠. 꼭 파이썬을 써야 할 이유가 있나?
C C++ 자바 아마 스팍스에 들어 올 정도면 이 언어들의 이름은 최소한 들어 보긴 했겠지요. 그럼…
C C++ 자바 파이썬? 파이썬이 얘네랑 뭐가 다르냐? 뭐 프로젝트를 하는데 파이썬을 쓴다면 파이썬이 뭔가 다른 언어보다 좋은 게 있으니까 쓰는 거 아녜요. 파이썬이 앞에 있는 언어들과 크게 다른 점을 든다면,
스크립팅 언어 스크립팅 언어라는 걸 들 수 있어요. 즉,
빠르게 짜고 빠르게 확인하고 빠르게 고친다 이 뜻이지요. 원래 프로그래밍이라는 건 그냥 코드를 짜는 게 끝이 아니에요. 결과가 잘 나오고 결과가 잘 안 나오면 고쳐야 프로그래밍이죠. 그러니까 이 세 가지가 모두 빨라야 빠른 프로그래밍이 가능한 거죠.
print "Hello, world!" 백문이 불여 일견이라고 한 번 짜 봅시다. 보면 알겠지만 이게 코드에요. 이걸 읽을 때는 그냥 영어 읽듯이 읽으면 되고, 헬로 월드라는 걸 쓰라는 말로 읽으면 되겠죠. 짜는 건 됐으니까 이제 얼마나 빨리 확인할 수 있는지 봅시다. [파이썬 콘솔을 연다 (절대로 shortcut으로 열지 말고 메뉴 보여 줄 것)] 여기에 꺽쇠 세 개 나오는 게 “너 코드 입력해라”라는 뜻이에요. 전문용어로 프롬포트라고들 그러죠. 그럼 앞에 썼던 코드를 입력해 봅시다. [코드를 친다] 잘 나오죠? 빨리 확인할 수 있다는 얘긴 바로 이겁니다.
http://python.org/ 물론 제가 지금 뭔가 창 띄우고 했으니까 제 컴퓨터에는 이걸 해 주는 프로그램이 있단 소리죠. 이 프로그램을 받으려면 여기 들어 가서,
여기 동그라미 쳐진 링크를 누르면 받아집니다. 링크 많은데 다 필요 없고 Windows Installer라고 되어 있는 거 받아서 다음만 계속 누르면 돼요. 참 쉽죠. 미리 받아 두신 분들이 좀 있으리라 믿지만 안 받으셨다면 가급적 지금 받아 두세요.
계산기 받는 동안 다른 예를 들어 볼께요. 설마 헬로 월드 출력하려고 파이썬을 배우는 건 아니니까 좀 더 쓸만한 걸 알려 줄게요. [좀 쉬고] 아까 전에 봤듯이 얜 피드백이 빨라요. 그럼 계산기로도 쓸 수 있겠죠.
>>> 3 + 4 * (5 - 6) -1 덧셈 뺄셈 곱셈 이런 건 기본적이라 그냥 할 수 있을 거에요. 곱셈이 가위표가 아니라 별표라는 것만 신경쓰면 어렵지 않겠죠. 나눗셈은 보통 슬래시를 쓰는데 이건 좀 설명이 필요해요. 왜냐하면…
>>> 8/5 1 …우리가 생각하는 대로 바로 나오지 않거든요. 시작하자마자 어려운 게 나오네? 나는 여기서 빠져 나가겠어. 안 되잖아? …음 이게 아니라, 사실 이건 나눗셈이 아니라 몫이라고 합니다. 그럼 나머지가 있겠죠.
>>> 8/5 1 >>> 8%5 3 나머지는 퍼센트 기호를 쓰는데 이걸 보면 아하 이게 몫이구나 알 수 있겠죠. 근데 좀 더 재밌는 것은…
>>> 8.0 / 5.0 1.6000000000000001 원하는 대로 1.6 비스무리한 게 나와요. 자리가 좀 많이 나오는데 이건 나중에 컴퓨터에서 소수점을 어떻게 표현하는지 알게 되면 이해할 수 있게 될 거니 생략하고, 일단은 소수점을 붙여야 소수점이 나온다는 걸 이해하면 되겠죠. 그럼 한 쪽에만 소수점을 붙이면 어떻게 될까요?
>>> 8.0 / 5 1.6000000000000001 >>> 8 / 5.0 하나만 소수점이 붙어 있어도 소수점 붙은 게 나오네요? 어쩌면 나눗셈 말고 덧셈 뺄셈 곱셈도 마찬가지 아닐까요? 해 봅시다. [이번엔 콘솔 열 것] 역시 그렇네요.
5 → 5.0 (O) 5 ← 5.0 (X) 그러니까 얘네는 소수점 붙은 숫자가 있으면 소수점 안 붙은 숫자에게 소수점이 “전염”되는 거죠. 반대로는 안 되고요. 얘네들의 성질입니다. 이걸 좀 간지나게 말하면…
자료형 자료형 내지는 타입이라고 하는 게 되지요. 여기서 우리는 정수라는 자료형과 실수라는 다른 자료형 사이의 관계를 다루고 있는 겁니다.
>>> "foo" + 'bar' 'foobar' 정수와 실수가 자료형이라고 했으니까 아까 전에 따옴표로 묶었었던 것도 또 다른 자료형이겠네요? 얘네는 문자열이라고 합니다. 문자가 이어져 있단 소리죠. 문자열은 덧셈으로 잇거나,
>>> "foo" * 4 'foofoofoofoo' 정수랑 곱하면 반복할 수 있습니다. 이거 말고도 이런 저런 게 있는데 직접 찾아 보시고요.
>>> 1 + 1 == 2 True >>> 1 + 1 == 3 False
참과 거짓: True, False 정수: 1, 2, -3, -4, … 실수: 1.0, -2.34, … 문자열: 'x', "Hello", … 기타 등등 지금까지 알게 된 자료형을 요약하면 이렇게 되겠습니다. 자 이제 이걸 가지고 한 번 뭔가를 해 볼까요.
>>> 1600000 + (3.0 - 2.05) * 6300000 7585000.0000000009 요즘 학점 떨어지면 돈 많이 낸다고 뭐라고 하죠. 제가 입학할 적에는 그런 거 없었는데 좀 끔찍하네요. 아무튼 제가 첫 학기에 놀고 먹다가 2.05라는 전대미문의 학점을 만들었는데 지금 이 학점을 받으면 얼마를 내야 하는지 계산해 보면 이렇게 되겠습니다.
내가 니 학점을 어찌 아니? 근데 이건 제 얘기고, 딴 사람 학점을 갖고 계산하려면? 저기 2.05라고 쓰여져 있는 걸 고치면 되긴 하겠죠. 근데 2.05가 학점인지 아니면 이만 오백원인지 알게 뭐에요? 그래서 여기에 이름을 붙여 줍니다.
>>> 1600000 + (3.0 - grade) * 6300000 7585000.0000000009
>>> 1600000 + (3.0 - grade) * 6300000 7585000.0000000009 2292999.9999999991 한 번 값을 정한 이름은 나중에 바꿀 수도 있고 물론 바뀐 뒤의 값으로 간주됩니다. 이런 식으로 값이 바뀔 수 있는 이름을 우리는…
변수 변수라고 합니다. 말 그대로 바뀔 수 있는 뭔가를 뜻하는 거지요. 변수 이름은 보통 생각하는 아무 거나 할 수 있지만 그래도 웬만하면 자기도 알아보기 쉽고 남들도 알아보기 쉬운 적절한 이름을 쓰도록 합니다. 이 경우 학점이니까 grade라고 지었죠?
>>> 1600000 + (3.0 - grade) * 6300000 -1550000.0 그런데 방금 전 짠 수식에는 헛점이 하나 있어요. 학점을 3.5라고 썼더니 마이너스가 나오네요? 설마 학점 잘 나온다고 학교에서 돈을 퍼 주는 건 아니겠죠? 그래서 우리는 학점이 3.0보다 크면 다른 수식을 적용하는 뭔가가 필요해요.
>>> if grade < 3.0: ... 1600000 + (3.0 - grade) * 6300000 ... else: ... 1600000 ... 1600000 그래서 조건문이라 불리는 이런 구조가 등장합니다. 파이썬 프롬포트에서 이렇게 여러 줄을 입력할 때는 프롬포트가 꺾쇠 괄호 대신에 점 세개로 나오고 그 상태에서 빈 줄, 그러니까 엔터를 두 번 치면 끝난다는 점을 기억해 두어야 합니다. 앞으로도 이런 식으로 하는 게 자주 나올 거에요. 하여튼 조건문을 보면 if 뒤에 조건, 그 다음 줄에 참일 때 실행될 것, 그 다음 다음 줄에 거짓일 때 실행될 것이 보입니다. 중요한 것은 참일 때나 거짓일 때 실행될 코드는 앞에 공백으로 들여쓰기가 되어 있다는 건데 이건 파이썬의 중요한 특징 중 하나입니다. 취향 따라 탭을 쓰셔도 되고 공백을 쓰셔도 되지만 만약 여러 줄을 같은 들여쓰기로 맞춘다면 같은 공백 개수로 맞춰야 한다는 점에 유의하셔야 겠습니다. 그래서 볼 수 있듯 제대로 된 값이 나왔지요.
>>> if grade < 3.0: ... 1600000 + (3.0 - grade) * 6300000 ... else: ... 1600000 ... 8782000.0 반대로 학점이 2.0보다 낮으면 어떻게 될까요? 아… 이거 너무 심한 거 아닙니까? 언제는 790만원이 최대 한도라고 해 놓고서 800만원 넘게 받는 건 말이 안 되겠죠. 학교라고 설마 이런 배짱이 있을리는 없으니까 이 코드도 수정해야 겠습니다.
>>> if grade < 2.0: ... 1600000 + (3.0 - 2.0) * 6300000 ... 1600000 + (3.0 - 2.0) * 6300000 ... elif grade < 3.0: ... 1600000 + (3.0 - grade) * 6300000 ... else: ... 1600000 ... 7900000.0 이제 세 개의 조건이 생겼는데요, 하나는 2.0보다 낮은 경우, 하나는 2.0 이상 3.0 이하인 경우, 마지막으로 3.0 이상인 경우로 나뉘겠습니다. 저기 보이는 elif라는 건 else if를 줄여서 그렇게 쓰는 겁니다.
>>> if grade < 3.0: ... if grade < 2.0: ... 1600000 + (3.0-2.0) * 6300000 ... else: ... 1600000 + (3.0-grade) * 6300000 ... else: ... 1600000 ... 7900000.0 물론 원한다면 이런 식으로 if문을 두 번 겹쳐 쓸 수 있어요. If 문을 겹쳐 쓸 경우 들여쓰기도 그만큼 해 줘야 한다는 걸 잊지 말아야 겠지요.
>>> for i in range(13): ... print i+1,'인의 아해가 거리를 질주하오.' ... 1 인의 아해가 거리를 질주하오. 2 인의 아해가 거리를 질주하오. (생략) 13 인의 아해가 거리를 질주하오. 조건문 말고도 파이썬에는 반복문이 있는데, 이 문법에 대해서는 좀 있다가 설명을 하게 될테니 지금은 일단 “0부터 13보다 작은 숫자까지를 i에 넣어서 반복해라”라는 뜻으로 생각하도록 합시다. 우리는 1부터 13까지의 숫자가 필요하니까 i에 1을 더해야 제대로 된 값이 나오겠지요. 그리고 print 문에 콤마로 두 개의 값을 주는 걸 볼 수 있는데 이 경우 두 값 사이에는 공백이 들어 가게 됩니다.
one = (i%10==3 or i%10==6 or i%10==9) for i in range(1,101): one = (i%10==3 or i%10==6 or i%10==9) ten = (i/10==3 or i/10==6 or i/10==9) if one and ten: print '짝짝' elif one or ten: print '짝' else: print i 지금까지 배운 것으로 한 번 삼육구 게임을 하는 프로그램을 만들어 보겠습니다. 1의 자리가 3, 6, 9이거나 10의 자리가 3, 6, 9이면 박수를 쳐야 하고, 둘 다 3, 6, 9중 하나이면 두 번 쳐야 하는 거니 이렇게 되겠지요. 여기서 and와 or는 잘 알듯이 둘 다 참이면 참이 되거나, 둘 중 하나만 참이면 참이 되는 연산자입니다. 또 하나 유념해 두어야 할 것은 제가 range 뒤에 1 콤마라고 붙여 놓은 게 있는데, 이건 1부터 시작하라는 표시입니다. 따라서 i는 1부터 101보다 작은 수, 즉 100까지 바뀌게 됩니다.
너무 복잡하네 …근데 써 놓고 보니 좀 복잡하네요. 물론 나중에 프로그래밍을 하게 되면 이 정도는 껌이라고 생각할진 모르겠지만 일단 나눗셈과 나머지 연산이 난무하는 것이 영 보기가 좋지 않죠. 그래서 우리는 지금부터…
Refactoring? 리팩토링이라는 것을 할 거에요. 리팩토링이라고 하는 건 프로그램을 좀 더 다루기 좋도록 정리하는 작업이라 할 수 있는데, 앞으로 프로그래밍을 하게 되면 이미 잘 돌아 가는 것처럼 보이는 프로그램도 정리해야 할 때가 생기게 될 거에요. 그 때를 위한 연습이라고 해 두죠.
# x가 3, 6, 9이면 True 아니면 False def is369(x): return x==3 or x==6 or x==9 리팩토링의 가장 기본적인 건 중복되는 코드를 “쳐 내는” 거에요. 이렇게 쳐 낸 코드는 파이썬에서는 함수라고 하는 것으로 표현되는데 이런 식으로 표현됩니다. 저기서 is369 뒤에 있는 x라는 것은 일종의 변수인데, 이 함수 안에서는 x라는 것이 이미 있다고 가정한다는 뜻이에요. 그리고 return 문은 이 함수를 x라는 것을 갖고 뭔가를 한 결과가 저렇게 된다는 걸 나타내는 거고요. 위에 샵 부호 뒤에 주절주절 쓰여 있는 건 주석이라고 해서 실행은 안 되지만 코드를 설명할 때 쓰는데, 이번 세미나에서는 귀찮아서 웬만하면 생략했어요. 주석을 언제 쓰고 언제 안 써야 할지는 앞으로 차차 익히게 될 거니까 일단 거기에 대해서는 말을 아끼도록 하겠습니다. 하여튼 이 함수의 역할은 x가 3, 6, 9 중 하나면 참이 된다고 주석에 써 있으니 한 번 확인해 보세요.
def is369(x): return x==3 or x==6 or x==9 for i in range(1,101): one = is369(i%10) ten = is369(i/10) if one and ten: print '짝짝' elif one or ten: print '짝' else: print i 함수를 도입해서 코드를 정리하면 이렇게 됩니다. Is369에 i의 1의 자리를 넣어서 그 결과를 one이라 하고, 10의 자리를 넣어서 그 결과를 ten이라고 하면 one과 ten 둘 다 참이면 짝짝, 둘 중 하나만 참이면 짝, 아니면 그냥 숫자를 출력한다는 걸 바로 알아 볼 수 있겠죠. 그리고 여기서 볼 수 있듯 콜론 뒤에 문장이 올 수 있는데 이건 한 문장으로 제한되니 짧은 문장을 넣는 데 적합하겠습니다.
return x==2 or x==3 or x==5 or x==7 for i in range(1,101): def is369(x): return x==2 or x==3 or x==5 or x==7 for i in range(1,101): one = is369(i%10) ten = is369(i/10) if one and ten: print '짝짝' elif one or ten: print '짝' else: print i 만약 이걸… 뭐 소수 게임이라고 해 두죠. 숫자에 2, 3, 5, 7 중 하나가 있으면 박수를 쳐야 하는 규칙이 들어 왔다고 칩시다. 아까 전의 복잡한 코드에서는 두 줄을 모두 고쳐야 했지만 여기서는 is369 함수만 고치면 간단하게 규칙을 반영할 수 있습니다.
return x==2 or x==3 or x==5 or x==7 for i in range(1,101): def needsclap(x): return x==2 or x==3 or x==5 or x==7 for i in range(1,101): one = needsclap(i%10) ten = needsclap(i/10) if one and ten: print '짝짝' elif one or ten: print '짝' else: print i 그런데 앞에서 is369라고 썼는데 이제는 2357이네요? 함수 이름이 실제 하는 일을 반영하지 못 하는 경우는 현실에서도 많이 발생하고, 이거 가지고 헷갈리면 꼼짝 없이 헛발질하는 거죠. 좀 더 사려 깊게 프로그램을 짰다면 needsclap 같이 좀 더 변화에 덜 민감한 이름을 썼을 겁니다. 만약 우리가 is369라는 이름을 그대로 썼다면, 누군가가 그걸 이름만 보고 함수를 가져다 썼을 때 문제가 생겨도 우리가 책임을 회피할 수 있을까요? 리팩토링은 그래서 필요한 겁니다. 앞으로도 자주 나올 거에요.
“함수” 단순히 코드를 묶는 것이 아닌 이미 다른 프로그래밍 언어에 경험이 있는 사람이라면 사실 이 세미나가 우스울 수도 있어요. 그런데 제가 말하고자 하는 건, 함수라는 게 보통 생각하는 것처럼 단순히 코드를 묶는 단위가 아니라는 거에요. 코드를 묶는다면 왜 묶는가? 어떻게 묶어야 잘 묶는 것인가?를 생각하자는 겁니다.
range도 함수인가요? 이 쯤에서 이런 질문이 나올 수 있는데, range도 이름 뒤에 괄호 열고 괄호 닫고 하는데 함수인지 궁금하실 수 있습니다. 직접 확인해 보면 어떨까요?
>>> range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 아까 전에 제가 for i in range(10)이 i를 0부터 9까지 변화시킨다고 했었죠. 여기에 그 해답이 나왔습니다. Range라는 건 함수인데 0부터 9까지의 숫자를 묶은 뭔가… 뭔가 복잡한 걸 반환하는 함수인 걸 알 수 있어요. 우리는 이걸 리스트라고 부르고, 다른 언어에서 배열이라고 하는 것들을 보통 이걸로 해결합니다.
>>> [4, 5] + ['hello?'] [4, 5, 'hello?'] >>> [1, 2, 3] [1, 2, 3] >>> [4, 5] + ['hello?'] [4, 5, 'hello?'] >>> [41, 42] * 3 [41, 42, 41, 42, 41, 42] >>> [] [] 리스트는 이런 식으로 대괄호를 묶어서 표현할 수 있고, 보통 숫자 쓰듯이 덧셈을 하거나 정수랑 곱셈을 할 수 있는데 전자는 두 리스트를 잇고 후자는 지정한 횟수만큼 리스트를 반복합니다. 빈 리스트도 이런 식으로 표현할 수 있고요.
>>> (4, 5) + ('hello?',) (4, 5, 'hello?') >>> (1, 2, 3) (1, 2, 3) >>> (4, 5) + ('hello?',) (4, 5, 'hello?') >>> (41, 42) * 3 (41, 42, 41, 42, 41, 42) >>> () () 키보드에 보면 대괄호 말고 묶는 것들이 몇 개 더 있죠. 중괄호도 있고 소괄호도 있는데 여기서는 소괄호를 봅시다. 소괄호로 묶은 건 튜플이라고 해서 리스트랑 많이 비슷한 자료형인데요, 연산자는 거의 비슷한데 한 가지 차이가 있다면 원소 한 개짜리 튜플은 그냥 괄호로 묶은 거랑 구분이 안 되기 때문에 괄호 안에 콤마를 하나 더 써 줘야지 튜플로 인식한다는 점입니다. 사실 파이썬은 뒤에 콤마 하나 붙은 것 정도로 불평하지 않는 좋은 언어인데 아마 스물 몇 장인가 뒤에 더 가면 그 예를 볼 수 있을 거에요.
>>> a[0] + a[1] + a[2] 6 >>> a[1] = 5 >>> a [1, 5, 3] 리스트의 각 원소를 접근하려면 리스트 값 뒤에 대괄호로 인덱스를 써 줍니다. 파이썬은 리스트의 원소를 셀 때 0부터 세는데 이건 사실 다른 언어에서도 많이 나오는 거니까 만약 익숙하지 않으시다면 익숙해지시는 게 좋을 거에요. 읽는 거랑 마찬가지로 리스트의 특정 원소를 바꾸는 것도 이런 식으로 가능합니다.
a a[0] a[-9] a[1] a[-8] a[2] a[-7] a[3] a[-6] a[4] a[-5] a[5] a[-4] 원소 접근을 좀 더 자세히 설명해 볼까요. 리스트의 모든 원소에는 인덱스가 두 개 붙어 있습니다. 하나는 앞에서부터 센 거고 하나는 뒤에서부터 센 건데요, 이 그림에서는 원소가 아홉개니까 처음부터 끝까지 0부터 8이라는 인덱스가 붙어 있고, 한편으로는 뒤에서부터 앞으로 -1부터 -9라는 인덱스가 붙어 있습니다. 그러니까 맨 마지막 원소는 항상 -1이라고 쓰면 접근할 수 있는 거죠.
>>> b[0] + b[1] + b[2] 6 >>> b[1] = 5 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment 그러면 리스트랑 튜플이 뭐가 다르냐? 튜플도 똑같이 인덱스로 접근할 수 있는 건 마찬가진데, 차이점이라면 이런 식으로 값을 쓰려고 했을 때 에러가 난다는 겁니다. 이 에러 메시지는 앞으로 오타를 내거나 하면 종종 보실 수 있을텐데 겁먹지 마시고, 보통 뭘 어디서 잘못했는지 잘 알려주니까 잘 읽고 고쳐 나가시면 되겠습니다.
>>> b = b[:1] + (5,) + b[2:] >>> b (1, 5, 3) 그러면 이미 있는 튜플을 바꾸려면 어떻게 해야 할까요? 이 때는 좀 번거로운 방법을 써야 하는데, 먼저 바꾸고자 하는 원소 앞에 있는 튜플이랑 그 뒤에 있는 튜플을 자르고 그것들을 새 값을 담은 튜플이랑 합쳐야 합니다. 콜론이 굉장히 신경쓰이지 않나요? 이게 리스트나 튜플의 일부분을 잘라 내는 슬라이싱 연산입니다.
a[3:8] a[0] a[-9] a[1] a[-8] a[2] a[-7] a[3] a[-6] a[4] a[-5] a[5] 슬라이싱 연산을 그림으로 표현하면 이렇게 되는데요… 이를테면 [3:8]은 3번 원소, 즉 네번째 원소부터 8번 원소, 즉 아홉번째 원소 바로 전까지의 원소를 잘라 내라는 뜻이에요. 8번 원소 바로 전까지면 7번이 마지막이 되겠지요. 만약 이걸 원소가 다섯개짜리인 리스트에 적용하면, 7번이 마지막이겠지만 7번 원소 같은 건 없으니까 마지막 원소인 4번 원소가 잘라낸 리스트의 마지막 원소가 됩니다.
a[-3:0:-2] a[0] a[-9] a[1] a[-8] a[2] a[-7] a[3] a[-6] a[4] a[-5] a[5] 콜론이 두 개 있으면 좀 더 복잡한 게 가능한데요, 이 경우 세번째 숫자는 건너 뛰는 정도를 결정하게 됩니다. 이건 좀 복잡하니까 그러려니 하고 넘어 가셔도 되는데, [-3:0:-2]의 경우 뒤에서 세번째 원소부터 시작해서 0번 원소 바로 직전, 즉 두번째 원소까지를… 마이너스 2니까 역으로 두 칸씩 건너 뛰면서 원소들을 가져 오라는 얘기가 됩니다. 그림으로 표현하면 이렇게 되는데 이걸로 이해가 되려나 모르겠네요.
사실 이런 거 몰라도 돼요 자 여기서 반전, 사실 이런 거 몰라도 돼요. 아까 전에 말한 콜론 두 개 들어 간 슬라이싱은 사실 많이 쓰는 건 아니고 파이썬이 그런 것도 지원하는구나 이 정도로만 알아도 사실 큰 문제는 없을 거라 생각하니까 이걸로 괜히 머리 쓰지 마세요. 다만 딱 하나 중요한 건 알아 두고 넘어 가죠.
a[::-1] a[0] a[-9] a[1] a[-8] a[2] a[-7] a[3] a[-6] a[4] a[-5] a[5] 콜론 두 개 들어 가는 슬라이싱의 특수한 예로 이런 식으로 리스트를 뒤집는 경우가 있는데요, 이 경우 맨 마지막에서 맨 첫 원소까지 역으로 훑어 가라는 거니까 당연히 결과는 리스트를 뒤집는 게 되겠지요. 이건 생각보다 많이 쓰이니까 봤을 때 당황하지 않도록 미리 말해 둡니다.
◀◀ 에… 너무 원래 주제에서 벗어난 것 같으니까 좀 뒤로 감겠습니다. 원래 하던 얘기는…
리스트와 튜플을 함께 쓸 이유 리스트와 튜플이 그런 성질을 갖고 있는 건 알겠는데 그게 무슨 소용이 있겠느냐 하는 거죠. 그래서 제가 예시를 하나 준비했습니다.
birthdays = [ ('강성훈', 1987, 9, 13), ('정재성', 1987, 2, 23), ('김준기', 1987, 5, 12), ] 요 리스트는 저를 포함해서 스팍스 05들 몇 명의 이름이랑 생년월일을 써 놓은 거에요. 여기서 볼 수 있듯 리스트는 “목록”을 나타내는 데, 튜플은 목록 안의 한 “항목”을 나타내는 데 주로 쓰입니다. 꼭 이렇게 해야 하는 건 아닌데 일반적으로 항목의 한 원소를 하나 하나 고치는 일은 별로 없는데 반해 목록에 원소를 추가하거나 빼내는 작업은 많이 하기 때문에 관례적으로 이렇게 굳어졌다고 생각하면 되겠어요. 그리고 앞에서 제가 파이썬이 콤마에 덜 인색하다고 했는데, 잘 보면 마지막 튜플 뒤에 콤마가 하나 더 있죠? 저렇게 쓰면 순서를 바꾸거나 더하거나 빼거나 할 때 실수로 콤마 빼 먹었다고 뭐라 뭐라 하지 않으니 좀 더 편하겠지요.
for entry in birthdays: name, year, month, day = entry print name, month, '월', day, '일생' 한 번 여기 나온 사람들의 이름이랑 생일을 출력해 보죠. 아까 전에 for in 뒤에 오는 range 어쩌구 하는 건 사실 함수 호출이라는 걸 배웠으니까, 그냥 변수를 써도 우리가 원하는 대로 작동할 거라고 알 수 있겠지요. 그리고 등호 옆에 콤마로 여러 개의 변수를 쓰는 것도 보이는데, 이건 오른쪽에 그 개수랑 맞는 리스트나 튜플 뭐 이런 게 있으면 자동으로 하나 하나 대입해 준다는 뜻입니다. 이 경우 entry는 순서대로 이름, 년, 월, 일로 이루어진 튜플이니까 하나 하나 변수랑 대응이 되겠지요. 이걸 흔히 튜플 언패킹이라고 부릅니다.
for name, year, month, day in birthdays: print name, month, '월', day, '일생' 아니면 이런 식으로 튜플 언패킹을 for in 문에 바로 끼워 넣는 것도 가능하겠고요. Entry를 직접 쓸 일이 없다면 이런 형태도 종종 쓸 수 있습니다.
for name, _, month, day in birthdays: print name, month, '월', day, '일생' 근데 자세히 보면 우리는 생년은 안 쓰잖아요? 뭐 다 같은 05인데 대강 대충 살자는 뜻에서 생년은 안 쓰는 것 같은데, 그러면 굳이 변수에다가 집어 넣을 필요가 없겠죠. 사실 안 쓰는 변수 이름이 딱히 정해져 있는 건 아니지만 이 경우 흔히 밑줄 하나를 변수 이름으로 대신 쓰곤 한다는 걸 알아 둡니다.
for name, _, month, day in birthdays: print '%s - %d월 %d일생' % \ 튜플이 리스트에 비해서 쓸만한 또 한 가지 이유는 이런 식으로 출력에 활용할 수 있다는 거에요. 여기서 퍼센트 연산자는 원래는 나눗셈의 나머지를 계산하는 연산자지만, 왼쪽에 문자열이 있고 오른쪽에 튜플이 있으면 저 %s나 %d 같은 것들에 각 값을 끼워 넣어서 문자열로 만들어 줍니다. 자세한 사항은 찾아 보셔야 겠지만 그냥 %s는 문자열을 출력하고 %d는 숫자를 출력한다고 생각하면 편하겠습니다. 그리고 또 하나 특이한 점은 퍼센트 뒤의 역슬래시인데, 이 역슬래시의 역할은 두 줄에 있는 문장을 하나로 이어 준다는 겁니다. 아니, 잠시만, 그럼 왜 좀 전에 birthdays 리스트 만들 때는 저걸 왜 안 썼나요? 그건 파이썬은 똑똑해서 괄호가 덜 닫혔으면 자동으로 뒷줄도 한 문장으로 인식하기 때문이에요. 하지만 이 경우에는 괄호로 묶인 게 아니니까 역슬래시를 써야 겠지요.
for name, _, month, day in birthdays: print '{0} - {1}월 {2}일생'.format( 똑같은 걸 이런 식으로 표현할 수도 있는데요, 이건 파이썬 최근 버전에 추가된 또 다른 문자열 포매팅 방법입니다. 만약 이게 안 된다면 파이썬 2.6 이전 버전이니까 알아서 잘 업데이트를 해 보시고… 여기서는 %s나 %d 같은 좀 헷갈리는 것 대신에 중괄호로 묶은 걸 쓰는 걸 볼 수 있는데 이게 좀 더 보기 편하겠지요. 그런데 문자열 뒤에 붙어 있는 저 .format이라는 것이 굉장히 거슬립니다. 함수처럼 쓰기는 하는 것 같긴 한데, 이게 뭘까요?
메소드 우리는 이걸 메소드라고 부릅니다. “왜” 메소드라고 부르는지는 차차 살펴 보아야 겠지만, 정의부터 대강 하면:
자료형이나 값과 연관된 함수 자료형이나 값과 연관된 함수라고 할 수 있습니다. 그러니까 메소드라는 건 함수의 일종인데 다른 값이랑 연결되어서 뭔가 요상한 걸 해 준다는 말이겠죠. 음… 이해가 잘 안 가죠?
formatfun = '{0} - {1}월 {2}일생'.format for name, _, month, day in birthdays: print formatfunc(name, month, day) 이런 식으로 고쳐 쓰면 좀 더 이해가 편할 것 같아요. 제가 따로 얘기는 안 했지만 파이썬에서 이런 식으로 함수를 변수에 넣거나 빼는 건 자유롭고, 심지어 함수에 함수 인자를 넣거나 함수에서 다른 함수를 반환하는 것도 자유로워요. 그래서 이런 식으로 특정한 값에 연관된 메소드를 변수에 넣는 것도 자유롭겠죠. 요컨대, 문자열의 format이라는 메소드는 연관된 문자열을 포맷 문자열로 해서 주어진 인자를 거기에 박아 넣어 문자열로 만들어 주는 함수라고 생각하시면 되겠습니다.
자료형 = 값 아까 함수가 값처럼 변수에 넣거나 빼거나 하는 게 자유롭다고 했는데, 사실 그건 그냥 함수가 값이기 때문이에요. 마찬가지로 자료형도 값으로 취급되는데…
>>> type('hello?') <type 'str'> <type 'int'> >>> type('hello?') <type 'str'> >>> type([1,2,3]) <type 'list'> 이를테면 이런 식으로, type 함수를 가지고 주어진 값의 자료형을 알아 낼 수 있습니다. 예를 들어 정수는 int라는 자료형을 가지고, 문자열은 str, 리스트는 list 뭐 이런 자료형을 가집니다. 튜플이나 참 거짓 같은 건 직접 확인해 보세요.
>>> type(4) == int True >>> type('hello?') == str >>> type([1,2,3]) == list 물론 이 이름은 변수 이름으로도 쓸 수 있습니다.
>>> str.format <method 'format' of 'str' objects> 아까 메소드가 값에 연관되는 것이라고 했는데 이걸 잘 보면 값 말고 값의 자료형에도 연관이 가능하다는 걸 알 수 있죠. 그럼 이 함수는 무슨 역할을 할까요?
for name, _, month, day in birthdays: # 엄밀하게는… fmt = '{0} - {1}월 {2}일생' for name, _, month, day in birthdays: # 엄밀하게는… print str.format(fmt, name, month, day) 사실 우리는 메소드가 “값에 연관된다”느니 하는 소리를 하고 있었지만 실상이라는 건 이런 겁니다. 그냥 str.format이라는 함수가 있고요, 이 함수의 첫 인자로 포맷 문자열을 넣으면 그 뒤의 것들을 가지고 포매팅을 해서 문자열로 돌려 주는 겁니다. 우리가 처음에 문자열 쩜 format 했던 것은 이걸 좀 더 간단하게 쓸 수 있게 해 주는 간편한 문법인 거죠. 사실 이게 제가 이렇게 말한 것처럼 간단한 문제는 아니지만, 지금은 이 정도만 알아도 충분할 겁니다.
>>> 'hello'.upper() 'HELLO' >>> _.lower() 'hello' >>> ' '.join(['we','are','the','world']) 'we are the world' >>> _.split(' ') ['we', 'are', 'the', 'world'] 문자열에는 format 말고도 다양한 메소드가 있는데 몇 개만 소개하자면, 대문자로 만드는 upper라거나, 소문자로 만드는 lower라거나, 리스트를 문자열로 변환하거나 반대로 역변환하는 join 및 split 메소드가 있어요. 코드가 쉬우니까 슬라이드 보고 직접 실험을 하면 어렵지 않을 겁니다. 그리고 밑줄 변수의 또 다른 활용이 나오는데… 밑줄 변수는 값을 유지할 필요 없는 임시 변수로도 쓰이지만 저런 식으로 아까 전에 나왔던 값을 저장하는 데도 쓰입니다. 그러니까 ‘hello’.upper()의 결과는 그 다음 줄에서 밑줄 변수에 저장되어 있는 거죠.
>>> len('hello') 5 >>> str(42) '42' >>> '%d' % 42 메소드는 아니지만 함수 중에서도 중요한 것들이 몇 개 있는데요, 이를테면 리스트나 튜플이나 문자열 길이를 돌려 주는 len 함수라거나, 숫자를 문자열로 바꿔 주는 str이라거나 이런 게 있습니다. Str은 아까 전에 자료형 이름이라고 했었는데 이런 용도로도 쓰네요. 사실 이 결과는 포매팅 연산자 쓴 거랑 같긴 한데 숫자 말고 다른 데도 쓸 수 있으니까 좀 더 범용적이겠죠.
>>> print str(1/5.0) 0.2 >>> 1/5.0 0.20000000000000001 >>> print repr(1/5.0) Str을 갖고 좀 놀아 보면 이런 식으로 차이가 나는 경우가 생기는데요, str을 쓰면 0.2이라고 나오는데 그냥 쓰면 0.2 뒤에 뭔가가 많이 붙는 경우입니다. 이건 얘네가 출력을 할 때 str을 쓰면 덜 정밀하게 출력하고 프롬포트에 쓰면 더 정밀하게 출력해서 이런 건데, 물론 여기에 대응되는 함수도 있습니다. Repr이라고 부르죠. Repr은 그 출력이 보통 더 자세하고, 보통 개발할 때 쓰기 편하게 만들어져 있다는 점에서 str과 구분됩니다.
def names(birthdays): result = [] for name, _, _, _ in birthdays: result.append(name) result.sort() return result 리스트에도 물론 메소드가 있습니다. 이를테면 리스트 끄트머리에 원소를 추가하는 append나, 전체 리스트를 순서대로 정렬하는 sort가 있지요. 튜플의 경우 앞에 있는 순서대로 비교하기 때문에 보통 이름이 앞에 가면 순서도 앞으로 가게 될 겁니다. 그러니 이 함수는 목록을 가져다가 이름만 정렬해서 반환하는 함수라고 할 수 있겠지요. 이거 말고도 insert라거나 count라거나 index라거나 이런 메소드들이 있는데 직접 찾아 봅시다.
>>> names(birthdays) ['\xb0\xad\xbc\xba\xc8\xc6', '\xb1\xe8\xc1\xd8\xb1\xe2', '\xc1\xa4\xc0\xe7\xbc\xba'] 이 함수의 결과를 출력하려면 좀 삽을 파야 합니다. 왜냐하면 아무 생각 없이 출력을 해 보면 repr의 결과를 쓰는데, 이 repr이라는 것이 한글 출력과는 영 연이 없어서 이런 식으로 내부 표현을 출력하거든요. 그래서 이걸 그대로 출력하는 건 별로 도움이 안 되고…
>>> for name in names(birthdays): ... print name ... 강성훈 김준기 정재성 이런 식으로 각 원소를 print 문으로 출력해야 합니다.
원래 세상 다 이런 거지 뭐 원래 세상 다 이런 거죠. 이 문제랑 관련해서는 앞으로 다른 세미나에서 자세한 얘기를 할 수 있을지도 모르겠습니다만 여기서는 그러려니 하고 넘어 갑시다.
def printbirthdays(birthdays): for name, _, month, day in birthdays: print '%s - %d월 %d일생' % \ (name, month, day) 세상 타령은 여기까지 하고 이번에는 출력하는 함수를 짜 봅시다. 이런 식으로 반환값이 없는 함수도 만들 수 있는데 어떤 언어에서는 이걸 프로시저라고 부르기도 합니다. 하지만 우리는 귀찮으니 그냥 함수라고 하죠. 뭐 코드는 아까 전에 나온 거랑 크게 다른 게 없죠?
def filterbyyear(birthdays, targetyear): result = [] for entry in birthdays: _, year, _, _ = entry if year == targetyear: result.append(entry) return result
def filterbyyear(birthdays, targetyear): def func(entry): _, year, _, _ = entry return year == targetyear return filter(func, birthdays)
def filterbyyear(birthdays, targetyear): def func((name, year, month, day)): return year == targetyear return filter(func, birthdays)
def filterbyyear(birthdays, targetyear): return filter( lambda (n,y,m,d): y==targetyear, birthdays)
def filterbyyear(birthdays, targetyear): return [(name, year, month, day) for name, year, month, day in birthdays if year == targetyear]
스크립팅 언어? 스크립팅 언어라는 걸 들 수 있어요. 즉,
스크립팅 언어 스크립팅 언어라는 걸 들 수 있어요. 즉,
“스크립팅할때 쓰면 스크립팅 언어”
목표
생년월일 검색하는 프로그램
보통은 이름과 생년월일을 모두 보여 준다 이름만 보여 줄 수도 있다 이름으로 검색할 수 있다 생년별 목록도 볼 수 있다
birthdays = [ ('강성훈', 1987, 9, 13), ('정재성', 1987, 2, 23), ('김준기', 1987, 5, 12), ('안병욱', 1989, 10, 14), ('강철', 1990, 3, 11), ('유수형', 1991, 3, 13), ('조유정', 1990, 4, 18), ('김도국', 1990, 3, 11), ]
데이터는 data.py 프로그램은 birthday.py
출력할 때는 꼭 print 명령을 써야 한다
# coding=cp949 ← utf-8이어야 할 수도 있음 birthdays = [ ('강성훈', 1987, 9, 13), ('정재성', 1987, 2, 23), ('김준기', 1987, 5, 12), ('안병욱', 1989, 10, 14), ('강철', 1990, 3, 11), ('유수형', 1991, 3, 13), ('조유정', 1990, 4, 18), ('김도국', 1990, 3, 11), ]
원래 세상 다 이런 거지 (2)
>>> import data >>> print '%d명' % len(data.birthdays) 8명
# coding=cp949 import data print '%d명' % len(data.birthdays)
# coding=cp949 from data import birthdays print '%d명' % len(birthdays)
모듈
import data data.py birthday.py data __main__
import birthday import data birthday.py persons.py birthday __main__ data.py data
# coding=cp949 from data import birthdays def main(): print '%d명' % len(birthdays) if __name__ == '__main__': main()
import data data.py birthday.py data __main__ data.pyc
def main(): choice = showmenu() if choice == 1: printnames(birthdays) elif choice == 2: printbirthdays(birthdays) elif choice == 3: printbyname(birthdays) elif choice == 4: printbyyear(birthdays)
def printnames(birthdays): pass # 이름만 출력할 것 def printbirthdays(birthdays): pass # 이름과 생년월일을 출력할 것 def printbyname(birthdays): pass # 이름을 입력받아서 # 해당하는 생년월일을 출력 def printbyyear(birthdays): pass # 생년을 입력받아서 # 해당하는 목록을 출력
def printnames(birthdays): print 'TODO: 이름 목록을 출력' def printbirthdays(birthdays): print 'TODO: 이름과 생년월일 출력' def printbyname(birthdays): print ('TODO: 이름을 입력받아 ' '해당하는 생년월일을 출력') def printbyyear(birthdays): print ('TODO: 생년을 입력받아 ' '해당하는 목록을 출력')
def showmenu(): print '---- 메뉴 ----' print '1. 이름 보기' print '2. 이름과 생년월일 보기' print '3. 이름으로 찾기' print '4. 생년으로 찾기' return input('>>> ')
---- 메뉴 ---- 1. 이름 보기 2. 이름과 생년월일 보기 3. 이름으로 찾기 4. 생년으로 찾기 >>> 2 TODO: 이름과 생년월일 출력
---- 메뉴 ---- 1. 이름 보기 2. 이름과 생년월일 보기 3. 이름으로 찾기 4. 생년으로 찾기 >>> 5
---- 메뉴 ---- 1. 이름 보기 2. 이름과 생년월일 보기 3. 이름으로 찾기 4. 생년으로 찾기 >>> 3*4-11 TODO: 이름 목록을 출력
---- 메뉴 ---- 1. 이름 보기 2. 이름과 생년월일 보기 3. 이름으로 찾기 4. 생년으로 찾기 >>> end Traceback (most recent call last): (생략) File "<string>", line 1, in <module> NameError: name 'end' is not defined
---- 메뉴 ---- 1. 이름 보기 2. 이름과 생년월일 보기 3. 이름으로 찾기 4. 생년으로 찾기 >>> [엔터] Traceback (most recent call last): (생략) File "<string>", line 0 ^ SyntaxError: unexpected EOF while parsing
input으로는 안 된다
>>> dir() ['__builtins__', '__doc__', '__name__', '__package__'] >>> dir(__builtins__) ['ArithmeticError', 'AssertionError', (…생략…), 'xrange', 'zip']
>>> [x for x in dir(__builtins__) ... if 'input' in x] ['input', 'raw_input']
>>> help(raw_input) Help on built-in function raw_input in module __builtin__: raw_input(...) raw_input([prompt]) -> string Read a string from standard input. The trailing newline is stripped. If the user hits EOF (Unix: Ctl-D, Windows: Ctl-Z+Return), raise EOFError. On Unix, GNU readline is used if enabled. The prompt string, if given, is printed without a trailing newline before reading.
모르는 게 있으면 우선 help를!
def inputnum(prompt): return input(prompt) def showmenu(): print '---- 메뉴 ----' print '1. 이름 보기' print '2. 이름과 생년월일 보기' print '3. 이름으로 찾기' print '4. 생년으로 찾기' return inputnum('>>> ')
def inputnum(prompt): return int(raw_input(prompt))
def inputnum(prompt): while True: # 무한반복 (사용에 주의!) line = raw_input(prompt) try: return int(line) except: print '숫자를 입력하시죠.'
예외 (exception) “일반적이지 않다”
>>> 3/0 Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDivisionError: integer division or modulo by zero
>>> asdf Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'asdf' is not defined
>>> int('notanumber') Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: invalid literal for int() with base 10: 'notanumber'
---- 메뉴 ---- 1. 이름 보기 2. 이름과 생년월일 보기 3. 이름으로 찾기 4. 생년으로 찾기 >>> exit 숫자를 입력하시죠. >>> quit >>> 나 좀 나가게 해 줘 ㅆㅂ
나가는 메뉴가 없다 Ctrl-C 같은 강제 종료키도 try~except가 먹어버린다
def showmenu(): print '---- 메뉴 ----' print '1. 이름 보기' print '2. 이름과 생년월일 보기' print '3. 이름으로 찾기' print '4. 생년으로 찾기' print '0. 끝내기' return inputnum('>>> ')
def inputnum(prompt): while True: line = raw_input(prompt) try: return int(line) except ValueError: print '숫자를 입력하시죠.'
방어적 프로그래밍
def names(birthdays): result = [] for name, _, _, _ in birthdays: result.append(name) result.sort() return result def printnames(birthdays): print ', '.join(names(birthdays))
def printbirthdays(birthdays): for name, _, month, day in birthdays: print '%s - %d월 %d일생' % \ (name, month, day)
def printbyname(birthdays): name = raw_input('이름을 입력하세요: ') for n, y, m, d in birthdays: if n == name: print '%s - %d월 %d일생' % \ (n, m, d)
def printbyname(birthdays): name = raw_input('이름을 입력하세요: ') count = 0 for n, y, m, d in birthdays: if n == name: print '%s - %d월 %d일생' % \ (n, m, d) count += 1 if count == 0: print '그런 사람이 없습니다.'
똑같은 기능에 똑같은 성능이면 간결한 게 낫다
def filterbyname(birthdays, targetname): return [(name, year, month, day) for name, year, month, day in birthdays if name == targetname] def printbyname(birthdays): name = raw_input('이름을 입력하세요: ') filtered = filterbyname(birthdays, name) printbirthdays(filtered)
def filterbyyear(birthdays, targetyear): return [(name, year, month, day) for name, year, month, day in birthdays if year == targetyear] def printbyyear(birthdays): year = inputnum('생년을 입력하세요: ') filtered = filterbyyear(birthdays, year) printbirthdays(filtered)
---- 메뉴 ---- 1. 이름 보기 2. 이름과 생년월일 보기 3. 이름으로 찾기 4. 생년으로 찾기 0. 끝내기 >>> 4 생년을 입력하세요: 1990 강철 - 3월 11일생 조유정 - 4월 18일생 김도국 - 3월 11일생
새 기능을 만들려면?
def main(): choice = showmenu() if choice == 1: printnames(birthdays) elif choice == 2: printbirthdays(birthdays) elif choice == 3: printbyname(birthdays) elif choice == 4: printbyyear(birthdays) 여기다 새 조건을 추가 def showmenu(): print '---- 메뉴 ----' print '1. 이름 보기' print '2. 이름과 생년월일 보기' print '3. 이름으로 찾기' print '4. 생년으로 찾기' print '0. 끝내기' return inputnum('>>> ') 여기다 새 메뉴를 추가
하나의 함수에 하나의 기능을
def showmenu(): print '---- 메뉴 ----' print '1. 이름 보기' print '2. 이름과 생년월일 보기' print '3. 이름으로 찾기' print '4. 생년으로 찾기' print '0. 끝내기' choice = inputnum('>>> ') if choice == 1: return printnames elif choice == 2: return printbirthdays elif choice == 3: return printbyname elif choice == 4: return printbyyear
CHOICES = {1: printnames, 2: printbirthdays, 3: printbyname, 4: printbyyear} def showmenu(): print '---- 메뉴 ----' print '1. 이름 보기' print '2. 이름과 생년월일 보기' print '3. 이름으로 찾기' print '4. 생년으로 찾기' print '0. 끝내기' choice = inputnum('>>> ') if choice in CHOICES: return CHOICES[choice]
def main(): routine = showmenu() if routine: routine(birthdays)
def main(): while True: routine = showmenu() if not routine: return routine(birthdays) print
main: 시작 함수 showmenu: 메뉴를 보여줌 inputnum: 숫자를 입력 받음 names: 목록에서 이름만 반환 filterby…: 목록을 조건에 따라 걸러냄 printbirthdays: 목록을 출력 print…: 각 메뉴를 구현함
여전히 중복이 있지 않나요?
for name,_,_,_ in birthdays: … for name,_,month,day in birthdays: … [(name,year,month,day) for name,year,month,day in birthdays if …]
순서를 하나라도 삐끗한다면?
새 자료형을 만들기
class person(object): def __init__(self, name, year, month, day): self.name = name self.year = year self.month = month self.day = day
자료형과 연관된 함수?
즉 그냥 함수 (첫 인자만 빼고)
class person(object): def __init__(you, name, year, month, day): you.name = name you.year = year you.month = month you.day = day
>>> p = person('강성훈', 1987, 9, 13) <__main__.person object at 0x00B12110> >>> print p.name 강성훈 >>> p.year, p.month, p.day (1987, 9, 13)
class person(object): def __init__(self, name, year, month, day): self.name = name self.year = year self.month = month self.day = day def __str__(self): return '%s - %d월 %d일생' % \ (self.name, self.month, self.day)
>>> print str(p) <__main__.person object at 0x00B12110>
파이썬 메모리의 구조
person p 환경 미지의 세계 자료형 person (클래스) name 값 person year month day 정수 9 정수 1987 정수 9 정수 13 문자열 '강성훈'
person p 환경 미지의 세계 자료형 person (클래스) name 값 person year month day
모든 것은 요 레퍼런스로
>>> a = [[]] * 3 >>> a [[], [], []] >>> a[0].append(4) [[4], [4], [4]]
>>> a = [[] for i in range(3)] [[], [], []] >>> a[0].append(4) >>> a[1].append(5) >>> a[2].append(6) [[4], [5], [6]]
제자리에서 변경이 가능한가?
그런 것: 리스트, 사전 안 그런 것: 문자열, 튜플
하여튼…
망한 값들 값 person (클래스) * person p 환경 미지의 세계 자료형 person
값 person (클래스) * 미지의 세계 자료형 person person p 환경
가비지 컬렉션 = 망한 값 처리하기
>>> p = person('강성훈', 1987, 9, 13) >>> print p.__str__() 강성훈 - 9월 13일생 >>> print str(p) >>> print p
마무리
# coding=cp949 class person(object): def __init__(self, …): … def __str__(self, …): … birthdays = [ person('강성훈', 1987, 9, 13), person('정재성', 1987, 2, 23), person('김준기', 1987, 5, 12), … ]
def printnames(birthdays): names = [p.name for p in birthdays] names.sort() print ', '.join(names) def printbirthdays(birthdays): for p in birthdays: print p
def printbyname(birthdays): name = raw_input('이름을 입력하세요: ') filtered = [p for p in birthdays if p.name == name] printbirthdays(filtered) def printbyyear(birthdays): year = inputnum('생년을 입력하세요: ') if p.year == year]
더 고칠 거리를 생각해 보세요!
끝…?
세상 모든 일이 이렇게 잘 풀리진 않겠지만…
리팩토링 테스팅 디버깅 문서화 ← 이건 안했지만
모듈을 꼭 우리만 만들란 법이 있나
“바퀴를 재발명하기”
>>> import sys >>> sys.version '2.6.2 (r262:71605, Apr 14 2009, 22:40:02) [MSC v.1500 32 bit (Intel)]'
>>> import math >>> math.sqrt(2) 1.4142135623730951 >>> 2 ** 0.5 >>> math.factorial(16) 20922789888000L
>>> import datetime >>> datetime.date.today() datetime.date(2010, 3, 12) >>> _.weekday() 4 >>> print ['월','화','수','목','금', ... '토','일'][_] 금
>>> import random >>> menus = ['짜장', '짬뽕', '짜짬면'] >>> print menus[random.randint(0, ... len(menus)-1)] 짜장 >>> print random.choice(menus) 짬뽕
>>> import urllib >>> for line in urllib.urlopen( ... 'http://www.census.gov/ipc/' ... 'www/popclockworld.html'): ... if 'worldnumber' in line: ... print line[45:-14] ... 6,807,870,286
레퍼런스는 정독해도 부족함이 없습니다
그 밖에 Numpy라거나 Django라거나 Pyglet이라거나…
안 다룬 것들
클래스에 대한 자세한 내용 set, unicode 같은 자료형들 외부 라이브러리 쓰기 패키지 다루기 그 밖에 파이썬을 쓰면서 필요하게 될 수많은 조언들
두 시간 안에 하긴 좀 벅찬지라…
질문과 답변?
감사합니다. 너무 길어서 정말로 죄송…
덤
슬라이드 목차 #1 시작 #4 왜 파이썬을 배우는가? #13 계산기로 쓰기, 자료형 #25 변수, 조건문, 반복문 #36 리팩토링, 함수 #44 리스트, 튜플 #58 메소드, 값으로서의 자료형 #72 내장 함수 및 메소드 #86 아젠다 설정 #92 모듈 #106 생년월일 프로그램의 뼈대 #116 내장 도움말 #120 리팩토링 (2), 방어적 프로그래밍, 예외 #132 생년월일 프로그램의 뼈대 (2) #140 리팩토링 (3) #148 클래스 #159 파이썬 메모리의 구조 #172 생년월일 프로그램의 마무리 #180 파이썬 내장 라이브러리 #191 안 다룬 것들
표기 컨벤션 코드 및 대화식 환경은 노란 배경으로, 프로그램 출력은 파란 배경으로, 직접 입력하지 않은 내용은 옅은 색으로, 이번 슬라이드에 처음 등장하는 문법·심볼은 붉은 색으로, (단, 공백 등은 별도 표시 안 함) 파이썬 프롬포트 및 예약어는 굵게 표기했음.