서 문 | 목 차 | 제 2 장 검사의 힘 >> |
다음 파이썬 프로그램은 완전하게 작동한다.
이해가 잘 안가실지도 모르겠지만, 걱정할 필요가 없다; 한줄 한줄 분해해 보겠다. 그러나 먼저 훓어보고, 무엇을 이해했는지 알아보자.
아직 그렇게 하지 못했다면, 이 예제와 더불어 이 책에서 사용되는 다른 예제들을 모두 내려 받을 수 있다 (Windows, UNIX, Mac OS).
def buildConnectionString(params): """Build a connection string from a dictionary of parameters. Returns string.""" return ";".join(["%s=%s" % (k, v) for k, v in params.items()]) if __name__ == "__main__": myParams = {"server":"mpilgrim", \ "database":"master", \ "uid":"sa", \ "pwd":"secret" \ } print buildConnectionString(myParams)
이제 이 프로그램을 실행시키고 무슨 일이 일어나는지 보자.
![]() | |
윈도우즈의 파이썬 IDE라면 모듈을 File->Run... (Ctrl-R)으로 실행할 수 있다. 출력은 대화형 창에 나타내어진다. |
![]() | |
맥 운영체제의 파이썬 IDE 에서는 모듈을 Python->Run window... (Cmd-R)으로 실행할 수 있다. 그러나 거기에는 먼저 설정해야할 중요한 선택사항이 있다. IDE에서 모듈을 열고, 창의 우-상 모서리에 있는 검은 삼각형을 클릭해서 모듈의 선택사항을 띄워라. 그리고 “Run as __main__” 이 체크되어 있나 확인하라. 이 설정은 그 모듈에 저장되기 때문에, 각 모듈에 대하여 한 번만 이렇게 해 주면 된다. |
![]() | |
(맥 OS X 를 포함하여) 유닉스 호환 시스템이라면 모듈을 명령어 라인으로부터 실행할 수 있다: python odbchelper.py |
Example 1.2. Output of odbchelper.py
server=mpilgrim;uid=sa;database=master;pwd=secret
파이썬은 다른 대부분의 언어들과 마찬가지로 함수를 가지고 있다. 그러나 C++ 과 같이 따로 헤더 파일이 없으며, 파스칼과 같이 interface/implementation와 같은 섹션이 없다. 함수가 필요하다면, 그냥 함수를 선언하고, 코드를 작성하면 된다.
Example 1.3. Declaring the buildConnectionString function
def buildConnectionString(params):
여기서 주목할 몇가지가 있다면, 첫째 def 키워드로 함수의 정의를 시작하며, 뒤이어서 함수의 이름이 따르고, 그 뒤에는 괄호로 둘러싸인 인자가 온다. (여기서 보여지지 않은) 다중 인자는 콤마로 분리한다.
둘째로, 함수는 반환 데이타의 형을 정의하지 않는다. 파이썬 함수는 반환 값에 데이타형을 지정하지 않는다; 심지어 값을 반환하는지 안 하는지도 지정하지 않는다. 사실 모든 파이썬 함수들은 값을 반환한다; 함수가 return 서술문을 실행한다면, 함수는 값을 반환할 것이다, 그렇지 않으면 파이썬의 null값인 None을 반환할 것이다.
![]() | |
비쥬얼 베이직에서, (값을 반환하는) 함수는 function으로 시작하고, 그리고 (값을 반환하지 않는) 서브루틴은 sub 로 시작한다. 파이썬에는 서브루틴이 없다. 모든 것은 함수이고, (심지어 그 값이 None일지라도) 모든 함수는 값을 반환한다. 그리고 모든 함수는 def 로 시작한다. |
셋째로, 인자 params는 데이타형을 지정하지 않는다. 파이썬에서 변수들은 명시적으로 유형이 정의되지 않는다. 파이썬은 변수가 어떤 형인지를 이해하고 내부적으로 기록 유지한다.
![]() | |
자바, C++, 그리고 기타 강력-형정의 언어에서는 함수의 반환값에 데이타 형과 각 함수의 인자들에 데이타 형을 지정해야만 한다. 파이썬에서는 명시적으로 어떤 데이타형도 지정하지 않는다. 어떤 값을 할당했느냐에 따라서 그 데이타의 형을 내부적으로 기록 유지한다. |
파이썬 함수에 doc string를 제공함으로써 문서화 할 수 있다.
Example 1.4. Defining the buildConnectionString function's doc string
def buildConnectionString(params): """Build a connection string from a dictionary of parameters. Returns string."""
세개짜리 인용부호는 여러-줄 문자열을 의미한다. 인용부호의 처음과 마지막 사이에 있는 모든 것은 나르개 복귀 문자 그리고 기타 인용부호 문자들을 포함하여, 하나의 문자열이다. 어느 곳에나 마음대로 사용할 수 있지만, 대부분의 사용처는 doc string을 정의할 때이다.
![]() | |
세개짜리 인용부호는 홑따옴표 그리고 겹따옴표로 하나의 문자열을 펄에서의 qq/.../와 같이 정의하기 쉽게 해준다. |
세개짜리 인용부호 사이에 있는 모든 것은 함수의 문서화 문자열(doc string)로서 그 함수가 무엇을 하는지 문서화 한다. doc string은 반드시 한 함수에서 가장 먼저 정의되어야 한다 (즉, 쌍점 다음 가장 첫 번째의 것). 기술적으로 함수에 문서화 문자열(doc string)은 필요없지만, 항상 달아 두는 것이 좋다. 이 충고를 그 동안 받았던 모든 프로그래밍 수업에서 지겹게 들어보았으리라고 생각한다. 하지만, 파이썬에는 여러분이 그렇게 해야할 동기가 제공된다: doc string은 실행시에 그 함수의 속성으로 사용가능하다.
![]() | |
많은 파이썬 IDE들은 doc string을 사용하여 문맥-감지 문서화를 제공하며, 그래서 한 함수의 이름을 타자하면, doc string이 풍선도움말로 나타난다. 이것은 대단히 도움을 주지만, 여러분이 doc string을 작성했을 때만이 유효하다. |
더 읽어야 할 것
이 말을 이해 못하겠다면, 단지 이렇게 말해 주겠다. 파이썬의 함수는 속성들을 가지며, 그러한 속성들은 실행시에 사용가능하다고 말이다.
파이썬의 다른 어떤 것들과 마찬가지로, 함수는 객체이다.
Example 1.5. Accessing the buildConnectionString function's doc string
>>> import odbchelper>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"} >>> print odbchelper.buildConnectionString(params)
server=mpilgrim;uid=sa;database=master;pwd=secret >>> print odbchelper.buildConnectionString.__doc__
Build a connection string from a dictionary Returns string.
![]() | |
파이썬의 import는 펄에서의 require와 비슷하다. 파이썬 모듈을 반입하면, 그 모듈의 함수에 module.function형태로 접근할 수 있다; 펄 모듈을 요구(require)하면, 그 모듈의 함수에 module::function의 형태로 접근하듯이 말이다. |
파이썬에서 모든 것은 객체이다. 그리고 거의 모든 것은 속성과 메쏘드를 가진다. [1] 모든 함수들은 내장 속성인 __doc__을 가지며, 그 함수의 소스 코드에서 정의된 문서화 문자열(doc string)을 반환한다.
이것은 너무 중요하기 때문에 잊어 버릴 경우를 대비해서 몇 번이고 반복해서 상기시켜 주겠다: 파이썬에서 모든 것은 객체이다. 스트링은 객체이다. 리스트는 객체이다. 함수는 객체이다. 심지어는 모듈도 객체이다. 간단하게 살펴보겠다.
더 읽어야 할 것
파이썬 함수는 명시적으로 begin 혹은 end를 전혀 가지지 않으며, 함수가 시작하고 끝나는 지점을 표시해주는 아무런 활괄호도 가지지 않는다. 유일한 식별자는 쌍점(“:”)이며 코드의 들여쓰기 그 자체이다.
Example 1.6. Indenting the buildConnectionString function
def buildConnectionString(params): """Build a connection string from a dictionary of parameters. Returns string.""" return ";".join(["%s=%s" % (k, v) for k, v in params.items()])
코드 블록들 (함수, if 서술문, for 회돌이, 등등)은 들여쓰기로 정의된다. 들여쓰기는 한 블록의 시작을 의미하며 들여쓰기를 원래대로 환원하면 블록이 끝난다; 명시적으로 괄호, 각괄호, 혹은 키워드가 없다. 이것은 공백이 중요하며, 그리고 일관성이 있어야 한다는 것을 뜻한다. 이 예제에서 함수 코드는 ( doc string을 포함하여) 4개의 공백으로 들여쓰기 된다. 반드시 4가 되어야 할 필요는 없고, 단지 일관성만 있으면 된다. 들여쓰기 되지 않은 첫 번째 줄은 함수의 바깥에 있다.
초기에 약간의 저항이 따르고 포트란과 비교하여 약간의 고난을 겪고나면 이것에 익숙해질 것이며 그 이점들을 보기 시작할 것이다. 가장 커다란 이점은 모든 파이썬 프로그램들은 비슷하게 보인다라는 것이다. 왜냐하면 들여쓰기는 언어의 필수조건이며 스타일의 문제가 아니기 때문이다. 이것으로 다른 사람들의 파이썬 코드를 더 쉽게 읽고 이해할 수 있다.
![]() | |
파이썬은 나르개 복귀 문자를 사용하여 서술문을 분리하고 쌍점과 들여쓰기를 사용하여 코드 블록을 분리한다. |
더 읽어야 할 것
파이썬 모듈은 객체이며 유용한 속성들을 가진다. 모듈을 작성할 때 이 속성들을 사용하여 쉽게 모듈을 테스트할 수 있다.
Example 1.7. The if __name__ trick
if __name__ == "__main__":
이 멋진 재료를 다루어 보기 전에 먼저 약간 살펴볼 것이 있다. 괄호는 if 표현식 주위에 필요하지 않다. 두 번째, if 서술문은 콜론으로 끝난다. 그리고 뒤에 들여쓰기된 코드가 따른다.
![]() | |
C 와 같이, 파이썬은 == 을 비교를 위해 사용하고 =를 할당을 위해 사용한다. C 와는 다르게 파이썬은 in-line 할당을 제공하지 않는다. 그래서 여러분이 비교하고 있다고 생각하는 값을 실수로 할당할 일은 없다. |
그러면 왜 이러한 특이한 if 서술문이 꼼수인가? 모듈은 객체이다. 그리고 모든 모듈은 내장 속성인 __name__을 가지고 있다. 한 모듈의 __name__은 그 모듈을 어떻게 이용하느냐에 달려 있다. 모듈을 반입하면, __name__은 그 모듈의 파일이름이며, 디렉토리 경로 혹은 파일 확장자는 없다. 그러나 그 모듈을 독립적인 프로그램으로 직접 실행시킬 수 있는데, 그 경우에 __name__은 특별한 기본 값으로 __main__이 된다.
Example 1.8. An imported module's __name__
>>> import odbchelper >>> odbchelper.__name__ 'odbchelper'
이 사실을을 알고 나면 이러한 if 서술문에 모듈을 배치함으로써 모듈 자체 안에서 그 모듈을 위한 테스트 모둠을 디자인할 수 있다. 모듈을 직접적으로 실행하면, __name__은 __main__이 되고, 그래서 테스트 모둠은 실행된다. 모듈을 반입하면, __name__은 다른 어떤 이름이 되고, 그래서 테스트 모둠은 무시된다. 이 때문에 새로운 모듈을 더 쉽게 개발하고 디버그한 다음 더 큰 프로그램으로 통합할 수 있다.
![]() | |
맥 파이썬에서는 if __name__ 꼼수를 작동시키기 위해서 단계가 하나 더 있다. 창의 우-상 모서리에 있는 검정색 삼각형을 클릭함으로써 모듈의 선택사항 메뉴를 띄워라, 그리고 확실히 __main__ 이 체크된 상태로 실행하라. |
더 읽어야 할 것
잠깐 샛길로 빠져보자. 왜냐하면 사전, 터플, 그리고 리스트에 관하여 알 필요가 있기 때문이다(오 세상에 이게 샛길이라니!). 여러분이 펄 해커라면, 아마도 사전과 리스트에 관한 정도는 슬쩍 넘겨 버릴 수도 있겠지만, 여전히 터플에 관하여 주의를 기울여야만 한다.
파이썬의 내장 데이타형 중의 하나는 사전이다. 사전은 키와 값들 사이의 관계를 일대일로 정의한다.
![]() | |
파이썬의 사전은 펄의 해쉬와 비슷하다. 펄에서 해쉬를 저장하고 있는 변수들은 항상 % 문자로 시작한다; 파이썬에서 변수는 마음대로 이름을 지을 수 있고, 데이타형을 내부적으로 기록 유지한다. |
![]() | |
파이썬에서 사전은 자바에 있는 Hashtable 클래스의 실체와 비슷하다. |
![]() | |
파이썬의 사전은 비쥬얼 베이직에 있는 Scripting.Dictionary 객체의 실체와 비슷하다. |
Example 1.9. Defining a dictionary
>>> d = {"server":"mpilgrim", "database":"master"}>>> d {'server': 'mpilgrim', 'database': 'master'} >>> d["server"]
'mpilgrim' >>> d["database"]
'master' >>> d["mpilgrim"]
Traceback (innermost last): File "<interactive input>", line 1, in ? KeyError: mpilgrim
Example 1.10. Modifying a dictionary
>>> d {'server': 'mpilgrim', 'database': 'master'} >>> d["database"] = "pubs">>> d {'server': 'mpilgrim', 'database': 'pubs'} >>> d["uid"] = "sa"
>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'pubs'}
주목할 것은 새로운 요소 (key uid, value sa)가 중앙에 나타난다는 것이다. 사실 요소들이 첫 번째 예제에서 순서대로 나타난 것은 단순한 우연의 일치였을 뿐이다; 지금은 순서대로 나타나지 않은 것도 단순히 똑 같은 우연의 일치이다.
![]() | |
사전은 요소들 사이에 순서의 개념을 전혀 가지지 않는다. 요소들이 “무질서 하다”라고 말하는 것도 부정확하다; 단순히 순서가 없을 뿐이다. 이것은 중요한 구별로서 사전에 있는 요소들에 특별한, 반복적인 순서로 (키에 의한 알파벳 순서로) 접근하려고 할때 신경이 쓰일 것이다. 이렇게 하는 방법은 여러가지가 있는데, 단순히 사전으로 구축되어 들어가지 않는다. |
Example 1.11. Mixing datatypes in a dictionary
>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'pubs'} >>> d["retrycount"] = 3>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 'retrycount': 3} >>> d[42] = "douglas"
>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 42: 'douglas', 'retrycount': 3}
Example 1.12. Deleting items from a dictionary
>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 42: 'douglas', 'retrycount': 3} >>> del d[42]>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 'retrycount': 3} >>> d.clear()
>>> d {}
![]() | del 으로 사전으로부터 키 값으로 항목들을 따로따로 삭제할 수 있다. |
![]() | clear 은 사전으로부터 모든 항목을 삭제한다. 주의할 것은 빈 활괄호의 집합은 아무런 요소도 없는 사전을 의미한다는 것이다. |
더 읽어야 할 것
리스트는 파이썬의 주요 데이타형이다. 리스트에 대한 유일한 경험이 비쥬얼 베이직에서의 배열 혹은 파워빌더에서의 데이타스토어라면, 파이썬의 리스트에 대하여 단단히 준비하라.
![]() | |
파이썬의 리스트는 펄의 배열과 비슷하다. 펄에서 배열을 저장하는 변수는 항상 @ 문자로 시작한다; 파이썬에서 변수는 어떤 이름이라도 될 수 있으며, 파이썬은 데이타형을 내부적으로 기록유지한다. |
![]() | |
(평생동안 리스트만 쓸 생각이라면 리스트를 배열처럼 사용할 수도 있겠지만) 파이썬의 리스트는 자바에서의 배열 그 이상이다). 훨씬 좋은 비유를 들자면 Vector 클래스일텐데, 이 클래스는 임의적인 객체들을 보유할 수 있고 새로운 항목이 추가될 때마다 동적으로 확장할 수 있다. |
>>> li = ["a", "b", "mpilgrim", "z", "example"]>>> li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li[0]
'a' >>> li[4]
'example'
Example 1.14. Negative list indices
>>> li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li[-1]'example' >>> li[-3]
'mpilgrim'
![]() | 음수 지표는 요소들을 리스트의 끝으로부터 거꾸로 세어서 접근한다. 비어있지 않은 모든 리스트의 마지막 요소는 항상 li[-1]이다. |
![]() | 만약 음의 지표가 혼란스럽다면, 다음 방식을 고려해 보라: li[n] == li[n - len(li)]. 그래서 이 리스트에서, li[2] == li[2 - 5] == li[-3] 이다. |
>>> li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li[1:3]['b', 'mpilgrim'] >>> li[1:-1]
['b', 'mpilgrim', 'z'] >>> li[0:3]
['a', 'b', 'mpilgrim']
Example 1.16. Slicing shorthand
>>> li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li[:3]['a', 'b', 'mpilgrim'] >>> li[3:]
['z', 'example'] >>> li[:]
['a', 'b', 'mpilgrim', 'z', 'example']
Example 1.17. Adding elements to a list
>>> li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li.append("new")>>> li ['a', 'b', 'mpilgrim', 'z', 'example', 'new'] >>> li.insert(2, "new")
>>> li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new'] >>> li.extend(["two", "elements"])
>>> li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements']
Example 1.18. Searching a list
>>> li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements'] >>> li.index("example")5 >>> li.index("new")
2 >>> li.index("c")
Traceback (innermost last): File "<interactive input>", line 1, in ? ValueError: list.index(x): x not in list >>> "c" in li
0
![]() | |
파이썬에는 불리언 데이타형이 없다(역주: 파이썬 2.2에서부터 불리언 형 도입). (if 서술문 같은) 불리언 문맥에서, 0 은 거짓이고 다른 모든 숫자들은 참이다. 이것은 다른 모든 데이타 형에도, 역시 확장된다. 빈 문자열 (""), 빈 리스트 ([]), 그리고 빈 사전 ({}) 은 모두 거짓이며; 다른 모든 문자열, 리스트 그리고 사전은 참이다. |
Example 1.19. Removing elements from a list
>>> li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements'] >>> li.remove("z")>>> li ['a', 'b', 'new', 'mpilgrim', 'example', 'new', 'two', 'elements'] >>> li.remove("new")
>>> li ['a', 'b', 'mpilgrim', 'example', 'new', 'two', 'elements'] >>> li.remove("c")
Traceback (innermost last): File "<interactive input>", line 1, in ? ValueError: list.remove(x): x not in list >>> li.pop()
'elements' >>> li ['a', 'b', 'mpilgrim', 'example', 'new', 'two']
>>> li = ['a', 'b', 'mpilgrim'] >>> li = li + ['example', 'new']>>> li ['a', 'b', 'mpilgrim', 'example', 'new'] >>> li += ['two']
>>> li ['a', 'b', 'mpilgrim', 'example', 'new', 'two'] >>> li = [1, 2] * 3
>>> li [1, 2, 1, 2, 1, 2]
더 읽어야 할 것
터플은 교환불능 리스트이다. 터플은 한 번 만들어지고 나면 어떠한 방식으로든 변경될 수 없다.
Example 1.21. Defining a tuple
>>> t = ("a", "b", "mpilgrim", "z", "example")>>> t ('a', 'b', 'mpilgrim', 'z', 'example') >>> t[0]
'a' >>> t[-1]
'example' >>> t[1:3]
('b', 'mpilgrim')
Example 1.22. Tuples have no methods
>>> t ('a', 'b', 'mpilgrim', 'z', 'example') >>> t.append("new")Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'tuple' object has no attribute 'append' >>> t.remove("z")
Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'tuple' object has no attribute 'remove' >>> t.index("example")
Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'tuple' object has no attribute 'index' >>> "z" in t
1
![]() | 터플에 요소를 추가할 수 없다. 터플은 append이나 extend 메쏘드가 없다. |
![]() | 터플로부터 요소들을 제거할 수 없다. 터플에는 remove 혹은 pop 메쏘드가 전혀 없다. |
![]() | 터플에서 요소들을 탐색할 수 없다. 터플에는 index 메쏘드가 전혀 없다. |
![]() | 그렇지만, in 을 사용하면 터플에 한 요소가 있는지 없는지 조사해 볼 수 있다. |
그래서 터플은 무엇에 필요한가?
![]() | |
터플은 리스트로 변환될 수 있으며, 그 반대도 가능하다. 내장 tuple 함수는 리스트를 취하고 똑 같은 요소들을 가지는 터플을 반환하며, 그리고 list 함수는 터플을 취해 리스트를 돌려준다. 사실상, tuple은 리스트를 얼리고, 그리고 list는 터플을 녹인다. |
더 읽어야 할 것
이제 사전, 터플, 그리고 리스트에 관하여 모든 것을 알았으므로 (오 세상에나!), 예제 프로그램 odbchelper.py으로 되돌아 가보자.
파이썬은 다른 대부분의 언어와 같이 지역 변수와 전역 변수를 가진다. 그러나 파이썬은 명시적인 변수 정의는 가지지 않는다. 변수는 한 값이 할당되면 튀어 나와 존재하다가, 영역을 벗어나게 될 때 자동으로 파괴된다.
Example 1.23. Defining the myParams variable
if __name__ == "__main__": myParams = {"server":"mpilgrim", \ "database":"master", \ "uid":"sa", \ "pwd":"secret" \ }
여기에는 몇가지의 재미있는 점들이 있다. 먼저, 들여쓰기에 주목하라. if 서술문은 코드 블록이며 함수와 똑 같이 들여쓰기될 필요가 있다.
둘째로, 변수는 줄연속 표식으로 작용하는 역사선(“\”)으로 여러줄에 걸쳐 나누어진 한개짜리 명령어이다.
![]() | |
하나의 명령어가 줄연속 표식인 (“\”)로 여러줄에 걸쳐 나누어질 때, 연속된 줄은 어떤 방식으로든 들여쓰기 될 수 있다; 파이썬의 정상적인 빡빡한 들여쓰기 규칙은 적용되지 않는다. 만약 파이썬 IDE가 연속된 줄을 자동-들여쓰기 한다면, 그렇게 하지 않을 심각한 이유를 가지지 않는한 아마도 그 기본 값을 받아들이는 것이 좋을 것이다. |
![]() | |
엄밀히 이야기 해서, (사전을 정의하는 것과 같은), 반괄호, 각괄호, 혹은 활괄호 안에 있는 표현식은 줄 연속 문자(“\”)가 있든 없든 여러줄로 분리될 수 있다. 나는 필요하지 않을 때 조차도 백슬래쉬를 포함시키는데 왜냐하면 그렇게 함으로써 코드를 더 쉽게 읽을 수 있다고 생각하기 때문이지만, 그것은 스타일의 문제이다. |
세번째로, myParams변수를 정의하지 않았다, 단지 하나의 값을 할당했을 따름이다. 이것은 VBScript에서 option explicit선택사항 없이 사용한 것과 비슷하다. 다행스럽게도, VBScript와는 다르게, 파이썬은 할당된 적이 없는 변수를 참조하지 못하도록 할 것이다; 그렇게 하면 예외가 일어난다.
Example 1.24. Referencing an unbound variable
>>> x Traceback (innermost last): File "<interactive input>", line 1, in ? NameError: There is no variable named 'x' >>> x = 1 >>> x 1
이것에 대해 언젠가는 파이썬에게 감사할 것이다
더 읽어야 할 것
파이썬에서 기가 막힌 프로그래밍의 지름길 중의 하나는 연속열을 사용하여 여러 값들을 한 번에 할당하는 것이다.
Example 1.25. Assigning multiple values at once
>>> v = ('a', 'b', 'e') >>> (x, y, z) = v>>> x 'a' >>> y 'b' >>> z 'e'
![]() | v 는 세 개의 요소를 가진 터플이다. (x, y, z) 는 세 개의 변수를 가진 터플이다. 하나를 다른 하나에 할당함으로써 v 의 각 값을 각각의 변수에 순서대로 할당한다. |
이것은 온갖 종류의 용도를 가진다. 나는 때때로 이름들을 일정범위의 값들에다 할당하고자 한다. C 에서라면 enum을 사용하고 그리고 수작업으로 각각의 상수와 그와 연관된 값들을 나열하려고 할 것이다. 그렇게 하는 일은 그 값들이 연속적일 때는 특히나 지루해 보인다. 파이썬에서는 내장된 range 함수를 여러-값 할당에 사용하면 빠르게 연속적인 값들을 할당할 수 있다.
Example 1.26. Assigning consecutive values
>>> range(7)[0, 1, 2, 3, 4, 5, 6] >>> (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)
>>> MONDAY
0 >>> TUESDAY 1 >>> SUNDAY 6
또한 다중-할당을 사용하여 단순히 모든 값들을 가진 터플을 반환함으로써 여러 값들을 반환하는 함수들을 구축할 수 있다. 달력은 그것을 하나의 터플로 취급할 수 있다. 또는 그 값들을 개별적인 변수들에 할당할 수 있다. os모듈을 포함하여 많은 표준 파이썬 라이브러리는 이런 방식을 사용한다. 이에 관해서는 제 3 장에서 논의해 보겠다.
더 읽어야 할 것
파이썬은 문자열에 삽입되는 형식화 값들을 제공한다. 아주 복잡한 표현식을 포함할 수 도 있지만, 가장 기본적인 사용법은 값들을 %s표시지정자로 문자열에 삽입하는 것이다.
![]() | |
파이썬에서 문자열 형식화는 C 에서의 sprintf함수와 똑 같은 구문을 사용한다. |
Example 1.27. Introducing string formatting
>>> k = "uid" >>> v = "sa" >>> "%s=%s" % (k, v)'uid=sa'
![]() | 전체 표현식은 문자열로 평가된다. 첫 번째 %s는 k의 값으로 대체되고; 두 번째 %s는 v의 값으로 대치된다. 문자열에 있는 모든 문자들은 (이 경우에는, 등호 표시) 그대로 유지된다. |
(k, v)는 터플이라는 것을 주목하라. 나는 이것이 무엇엔가 쓸모가 있다고 언급한 바 있다.
단순한 문자 연결을 하기 위하여 너무 많은 일을 한다고 생각할지도 모르겠다. 문자열 형식화가 그저 단순한 연결만이 아니라는 점만 뺀다면, 여러분이 옳을지도 모른다. 그것은 결코 단순한 문자 연결이 아니며 한 편으로 강제 형변환이기도 하다.
Example 1.28. String formatting vs. concatenating
>>> uid = "sa" >>> pwd = "secret" >>> print pwd + " is not a good password for " + uidsecret is not a good password for sa >>> print "%s is not a good password for %s" % (pwd, uid)
secret is not a good password for sa >>> userCount = 6 >>> print "Users connected: %d" % (userCount, )
![]()
Users connected: 6 >>> print "Users connected: " + userCount
Traceback (innermost last): File "<interactive input>", line 1, in ? TypeError: cannot add type "int" to string
더 읽어야 할 것
파이썬의 가장 강력한 특징중의 하나는 리스트 통합능력인데, 이것은 함수를 리스트의 각 요소에 적용함으로써 리스트를 다른 리스트와 짝짓는 간결한 방법을 제공해 준다.
Example 1.29. Introducing list comprehensions
>>> li = [1, 9, 8, 4] >>> [elem*2 for elem in li][2, 18, 16, 8] >>> li
[1, 9, 8, 4] >>> li = [elem*2 for elem in li]
>>> li [2, 18, 16, 8]
Example 1.30. List comprehensions in buildConnectionString
["%s=%s" % (k, v) for k, v in params.items()]
먼저, params 사전의 items함수를 호출하고 있는 것에 주목하자. 이 함수는 사전의 데이타를 모두 가진 터플의 리스트를 반환한다.
Example 1.31. keys, values, and items
>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"} >>> params.keys()['server', 'uid', 'database', 'pwd'] >>> params.values()
['mpilgrim', 'master', 'sa', 'secret'] >>> params.items()
[('server', 'mpilgrim'), ('uid', 'sa'), ('database', 'master'), ('pwd', 'secret')]
이제 buildConnectionString이 무엇을 하는지 살펴 보자. 이것은 리스트를 params.items()로서 취하고, 그 리스트를 새로운 리스트에 짝짓기 해서 문자열 형식화를 각 요소에 적용한다. 새로운 리스트는 params.items()와 똑 같은 개수의 요소를 가지지만, 새로운 리스트의 각 요소는 params 사전에서 유래한 키와 그와 연관된 값을 모두 포함하는 문자열이 될 것이다.
Example 1.32. List comprehensions in buildConnectionString, step by step
>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"} >>> params.items() [('server', 'mpilgrim'), ('uid', 'sa'), ('database', 'master'), ('pwd', 'secret')] >>> [k for k, v in params.items()]['server', 'uid', 'database', 'pwd'] >>> [v for k, v in params.items()]
['mpilgrim', 'sa', 'master', 'secret'] >>> ["%s=%s" % (k, v) for k, v in params.items()]
['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret']
![]() | 변수 두 개를 사용하여 params.items() 리스트를 반복한다는 것을 주목하라. 이것은 여러-변수 할당의 또 다른 사용법이다. params.items()의 첫번째 요소는 ('server', 'mpilgrim')이다, 그래서 리스트 통합의 첫 반복에서, k 는 'server'를 확보할 것이고 v 는 'mpilgrim'을 획득할 것이다. 이 경우에 v의 값을 무시하고 있으며 반환되는 리스트에 k 값만을 포함하고 있다. 그래서 이 리스트 통합은 params.keys()와 동등한 결과로 끝난다. (실제 코드라면 이와 같은 리스트 통합을 사용하지 않으려고 할지도 모르겠다; 그러나 이것은 대단히 간단한 예제이다. 여기에서 무슨일이 일어나는지 훓어 볼 수 있다.) |
![]() | 여기에서도 같은 일을 하지만, k의 값은 무시한다. 그래서 이 리스트 통합은 params.values()와 동등한 결과로 끝난다.. |
![]() | 위의 두 예제를 간단한 문자열 형식화로 결합하면, 사전의 각 요소의 키와 값 모두를 포함하는 문자열의 리스트를 얻는다. 이것은 의아하게도 프로그램의 출력결과와 비슷하게 보인다; 아직 남아 있는 일이라면 이 리스트의 구성요소들을 하나의 문자열로 결합하는 것이다. |
더 읽어야 할 것
키-값 쌍의 리스트는 key=value의 형태이다. 그것들을 하나의 문자열로 결합하고자 한다. 문자열의 어떤 리스트라도 한 개의 문자열로 결합하려면, 문자열 객체의 join 메쏘드를 사용하라.
Example 1.33. Joining a list in buildConnectionString
return ";".join(["%s=%s" % (k, v) for k, v in params.items()])
계속 하기 전에 주목할 만한 흥미로운 사실은 반복해서 말하지만 함수는 객체이며 문자열도 객체이고 모든 것이 객체라는 것이다. 문자열 변수(variables)도 객체라고 주장할지 모르겠다. 그러나 아니다. 다음 예제를 잘 살펴보면, ";" 문자열 그 자체가 객체라는 것을 알 것이다. 그리고 그 객체의 join 메쏘드를 호출하고 있다는 것을 알 것이다.
어쨋든, join 메쏘드는 리스트의 요소들을 하나의 문자열로 결합하는데, 각각의 요소들은 쌍-반점으로 분리된다. 구분자가 반드시 쌍-반점일 필요는 없다; 한개의 문자일 필요는 더더욱 없다. 어떠한 문자열도 될 수 있다.
![]() | |
join 은 문자열의 리스트에만 작동한다; 어떠한 강제 형변환도 하지 않는다. 문자열이 아닌 요소들을 하나 이상 가지고 있는 리스트를 결합하면(join) 예외가 일어난다. |
Example 1.34. Output of odbchelper.py
>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"} >>> ["%s=%s" % (k, v) for k, v in params.items()] ['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret'] >>> ";".join(["%s=%s" % (k, v) for k, v in params.items()]) server=mpilgrim;uid=sa;database=master;pwd=secret
그러면 이 문자열은 help 함수로부터 반환되어 호출블록에 의해 출력된다. 이 장을 읽기 시작할 때에 감탄했었던 그 출력을 보여 준다.
역사에 대한 환기 : 처음 파이썬을 배울 시절 나는 join이 리스트의 메쏘드가 되어야 한다고 예상했었다. 그러면 구분자를 인자로 취할 것이다. 많은 사람들이 같은 방식으로 생각한다. join 메쏘드 뒤에는 숨겨진 이야기가 있다. 파이썬 1.6 이전 버전에서 문자열은 이러한 모든 유용한 메쏘드들을 가지고 있지 않았다. 따로 string 모듈이 있었는데 모든 문자열 함수를 담고 있었다; 각 함수는 문자열을 자신의 첫번째 인자로 취했다. 함수는 문자열 보다 더 중요하게 여겨졌다. 그것은 lower, upper, 그리고 split과 같은 함수들에는 의미가 있었다. 그러나 많은 핵심 파이썬 프로그래머들은 그 새로운 join 메쏘드에 반대하면서, 그것은 대신에 리스트의 메쏘드가 되어야만 한다든가, 혹은 그것은 절대로 움직여선 안되고 단지 구형 string 모듈(여전히 많은 유용한 것들을 가지고 있다)의 일부에 머물러야 한다고 주장하였다. 나는 그 새로운 join 메쏘드를 단독으로 사용한다. 그러나 여러분은 같은 방식으로 작성되어진 코드를 볼 수 있을 것이다. 그리고 그 때문에 괴롭다면, 대신에 구형 string.join 함수를 사용하면 된다.
아마도 문자열을 리스트로 쪼개는 비슷한 메쏘드가 있는지 궁금할 것이다. 물론 있다. 그것은 split이라고 부른다.
Example 1.35. Splitting a string
>>> li = ['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret'] >>> s = ";".join(li) >>> s 'server=mpilgrim;uid=sa;database=master;pwd=secret' >>> s.split(";")['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret'] >>> s.split(";", 1)
['server=mpilgrim', 'uid=sa;database=master;pwd=secret']
![]() | split는 문자열을 여러-요소 리스트로 쪼개므로써 join의 반대가 된다. 구분자 (“;”)가 완전히 생략되었음을 주목하라; 그것은 반환된 리스트의 요소중 어느 것에도 나타나지 않는다. |
![]() | split는 쪼개질 횟수로 선택적인 두 번째 인자를 취한다. (“"음, 선택적 인자라...” 함수에서 이렇게 하는 법을 다음장에서 배워 보겠다.) |
![]() | |
anystring.split(delimiter, 1) 는 유용한 테크닉이다. 문자열을 검색해서 하부문자열을 찾고 그리고 그 하부문자열 앞의 모든 것들과 (반환된 리스트의 첫 번째 요소가 됨) 그 하부문자열 뒤의 모든 것들과 (두 번째 요소가 됨) 작업하고자 할 때 유용하다. |
더 읽어야 할 것
odbchelper.py 프로그램과 그 출력은 이제 완전히 이해가 되었으리라 믿는다.
def buildConnectionString(params): """Build a connection string from a dictionary of parameters. Returns string.""" return ";".join(["%s=%s" % (k, v) for k, v in params.items()]) if __name__ == "__main__": myParams = {"server":"mpilgrim", \ "database":"master", \ "uid":"sa", \ "pwd":"secret" \ } print buildConnectionString(myParams)
Example 1.37. Output of odbchelper.py
server=mpilgrim;uid=sa;database=master;pwd=secret
다음장으로 다이빙해 들어가기 전에, 편안하게 이러한 모든 것들을 하고 있는지 확인하라:
[1] 다른 프로그래미밍 언어들은 “객체(object)”를 다른 방식으로 정의한다. 어떤 언어에서는, 모든 객체는 반드시 속성과 메쏘드를 가져야 한다는 것을 의미한다; 다른 언어에서는, 모든 객체는 하부클래스화가 가능해야 한다는 것을 의미한다. 파이썬에서 그 정의는 훨씬 느슨하다; 어떤 객체는 속성도 메쏘드도 가지지 않는다 (이것에 관해서는 나중에 더 자세히 다룸), 그리고 모든 객체가 하부클래스화가 가능한 것도 아니다. (이것에 관해서는 제 3장에서 더 자세히 다룸). 그러나 모든 것은 객체이다. 변수에 할당되어 질 수 있고 인자로서 함수에 건네질수 있다는 점에서 (이것에 관해서는 제 2장에서 더 자세히 다룸) 모든 것은 객체이다.
[2] 실제로는 더 복잡하다. 사전의 키는 반드시 교환불가능해야 한다. 터플 자체는 교환불가능하지만, 리스트로 이루어진 터플을 가진다면, 그것은 교환가능으로 간주되고 그것을 사전의 키로 사용하는 것은 안전하지 않다. 오직 문자열, 숫자로 이루어진 터플들만이 혹은 다른 사전-적합 터플만이 사전의 키로 사용하는 것이 좋다.
서 문 | 목 차 | 제 2 장 검사의 힘 >> |