앞으로    목차    1    2    3    4    5    제6장    7    8    다음으로

6. 파이썬의 디자인

6.1. 왜 파이썬에는 switch나 case 서술문이 없나요?

쉽게 if... elif... elif... else를 사용하여 충분히 구현할 수 있습니다. switch 서술 구문에 관하여는 제안이 있었지만, 아직 범위 테스트를 해야할지 그리고 어떻게 해야하는지에 관하여는 (아직) 합의된 바 없습니다.


6.2. 왜 파이썬은 서술문의 그룹을 위하여 들여쓰기를 사용하나요?

기본적으로 제가 믿는 것은 그룹을 짓는데 들여쓰기를 사용하게 되면 평균적인 파이썬 프로그램이 정말 우아하고 아주 명료해진다고 믿고 있습니다. 대부분의 사람들은 잠시만 지나면 이 사양을 사랑하게 됩니다. 그것에 대해 약간 변호하면:

begin/end 괄호가 없으므로 해석기에 의하여 인지되는 구룹화와 읽는 사람사이의 불일치가 있을 수 없습니다. 오래전에 다음과 같은 C 코드를 본 기억이 납니다:

if (x <= y)
                x++;
                y--;
        z++;
오랫동안 들여다 보고서 x > y임에도 불구하고 왜 y가 감소하는지 궁금했습니다... (그때 나는 C 초보자가 아니었습니다.)

시작/끝 괄호가 없으므로, 파이썬은 코딩의 문제가 덜 합니다. C에서는 (일관성 때문에, 어떠한 경우에는 한개의 서술문 주위에도 괄호를 배치한다는 선택을 포함하여) 괄호를 놓는 수 많은 다른 방법이 있습니다. 하나의 스타일을 사용하는 코드를 읽고 (쓰고) 하는데에 익숙해지면, 또 다른 스타일을 읽을 때에 (또는 작성을 필요로 한다면) 적어도 약간은 불편할 것입니다. 많은 코딩 스타일은 시작/끝 괄호들은 한줄에 배치합니다. 이것때문에 프로그램은 상당히 길어지고 아까운 화면 공간을 낭비하여, 프로그램을 개괄해 보기가 더 어렵게 됩니다. 이상적으로, 함수는 한개의 tty 화면에 맞아야 합니다 (즉, 20 줄정도). 파이썬에서 20 줄은 C에서의 20줄 이상의 값어치를 합니다. 이것은 단지 시작/끝 괄호가 없어서가 아닙니다 (선언이 없다는 점도 도움이 됩니다, 그리고 물론 강력한 연산은 말할 것도 없고), 그러나 (없어도) 확실히 도움이 됩니다!


6.3. 왜 파이썬의 문자열은 변경불가한가요?

여기에는 두 가지의 이점이 있습니다. 하나는 성능입니다: 문자열이 변경불가라는 것을 알면 구축단계에서 설계하기가 편해집니다 -- 고정되고 변경되지 않는 기억장소 요구. (이것은 또한 터플과 리스트 사이의 구별의 이유중의 하나이기도 합니다.) 다른 하나는 파이썬에서 문자열은 숫자형태의 "요소"로 간주됩니다. 어떤 조작도 값 8 을 다른 어떤 것으로 변경시키지 않을 것입니다, 그리고 파이썬에서, 어떤 조작도 문자열 "eight"를 다른 어떤 것으로 변경시키지 않을 것입니다. (Jim Roskind로부터 인용)


6.4. 삭제됨


6.5. 왜 파이썬은 어떤 기능들에 대하여 메쏘드를 사용하고 (예로. list.index()) 다른 기능들에 대하여 함수를 사용하나요 (예로. len(list))?

함수는 일단의 형들에 포괄적으로 사용되는 연산들을 위하여 사용되면 심지어는 전혀 메쏘드를 가지지 않는 객체에 대하여도 작동할 것입니다 (예. 숫자, 문자열, 터플). 또한, len(), max(), min()들을 내장 함수로 구현한 것은 실제로 각 형에 대하여 메쏘드로 구현하는 것보도 코드가 덜 듭니다. 개별적인 경우들에 관하여는 변명도 있을수 있지만 이제는 그러한 것들을 근본적으로 변경하기에는 진짜 너무 늦었습니다.


6.6. 왜 내장형 (예. 리스트나 파일)으로부터 클래스를 파생할 수 없나요?

이것은 파이썬에 상대적으로 늦게 (사용자-정의) 클래스가 도입되었기 때문입니다 -- 구현 프레임워크는 쉽게 그것을 허용하지 않습니다. 진행-상황을 보려면 질문 4.2에 대한 답변을 보세요. 이점은 (가까운) 미래에 고쳐지리라고 생각합니다.


6.7. 왜 'self'가 메쏘드 정의와 호출에서 반드시 선언되어 명시적으로 사용되어야 하나요?

그래, 사용하시는 프로그래밍 언어가 C++ 인가요 Java인가요? :-) 클래스가 파이썬에 도입될 때, 이것은 (또다시) 메쏘드를 인터프리터에 너무 많은 변경없이 구현하는 가장 간결한 방법이었습니다. 이 아이디어는 Modula-3로부터 빌려 왔습니다. 지금에는, 많은 이유로, 대단히 유용한 것으로 증명되었습니다.

첫째로, 더욱 더 명료해진 것은 메쏘드나 실체 속성을 지역 변수 대신에 사용하고 있다는 것입니다. "self.x" 또는 "self.meth()"를 읽어보면 여러분이 그 클래스 정의를 기억하지 못하더라도 실체 변수 혹은 메쏘드가 사용된다는 사실이 절대적으로 명백해집니다. C++에서, 지역 변수 선언을 없애 봄으로써 약간은 알아볼 수 있습니다 (전역 변수가 별로 없거나 혹은 쉽게 인식될 수 있다는 가정하고) -- 그러나 파이썬에는, 지역 변수 선언이 없습니다, 그래서 그 클래스 정의를 확실하게 참조하여야할 필요가 있습니다

두번째로, 특정한 클래스로부터 메쏘드를 명시적으로 참조하거나 호출하기를 원한다고 할지라도 어떤 특별한 구문도 필요하지 않다는 것을 뜻합니다. C++에서, 파생된 크래스가 덮어 쓴 기본 클래스로부터 메쏘드를 사용하려고 한다면, :: 연산자를 사용해야 합니다 -- 파이썬에서는 baseclass.methodname(self, <argument list>)로 사용합니다. 이것은 특히 __init__() 메쏘드에 유용합니다, 그리고 일반적으로 파생된 클래스 메쏘드가 같은 이름을 가진 기본 클래스 메쏘드를 확장하기를 원하면 그리하여 어쨌든간 기본 클래스를 호출하여야만 하는 경우에 유용합니다.

마지막으로, 실체 변수에 대하여는, 할당에 대한 구문적 문제를 해결합니다: 파이썬에서 지역 변수는 (그 정의상!) 함수 몸체에 값이 할당된 변수들이므로 (그리고 명시적으로 전역변수로 선언되지 않으므로), 할당이 지역 변수대신에 실체 변수에게 할당되고자 함을 인터프리터에게 알려줄 어떤 방법이 있어야 합니다, 그리고 그것은 (효율성의 이유로) 구문적인 것이 더 좋습니다. C++은 이것을 선언으로 해결하지만, 파이썬은 선언을 가지고 있지 않으며 단지 이러한 목적을 위하여 선언을 도입해야만 한다면 불행한 일입니다. 명시적인 "self.var"를 사용하게 되면 이것은 멋지게 해결됩니다. 비슷하게, 실체 변수를 위해서는, "self.var"라고 쓰게 되면 한 메쏘드 안에 있는 허가되지 않은 이름들을 참조하기 위해서 그 실체의 디렉토리를 탐색할 필요가 없다는 것을 뜻합니다.


6.8. OS-종속적인 쓰레드 구현대신에 파이썬에서 쓰레드를 흉내낼 수 있습니까?

답변 1: 불행하게도, 파이썬은 각각의 파이썬 스택 프레임당 적어도 한 개의 C 스택 프레임을 밀어 넣습니다. 또한, 확장들은 거의 아무때나 파이썬으로 역 호출을 할 수 있습니다. 그러므로 완전한 쓰레드 구현은 C 에 대한 쓰레드 지원을 요구합니다.

답변 2: 다행스럽게도, 스택리스 파이썬이 있습니다, C 스택을 피해서 완전하게 다시 디자인된 인터프리터 회돌이를 가집니다. 여전히 실험적이기는 하지만 대단히 유망합니다. 비록 표준 파이썬과 이진 수준에서 호환가능함에도 불구하고, 스택리스 파이썬이 핵심부로 장착될지는 여전히 불명확합니다 -- 아마도 단지 너무 혁명적이기 때문일 것입니다. 스택리스 파이썬은 현재로 여기에 있습니다: http://www.stackless.com/. 이것을 사용하는 microthread 구현은 여기에서 찾아 볼 수 있습니다: http://world.std.com/~wware/uthread.html.


6.9. 왜 람다 형태는 서술문을 포함할 수 없나요?

파이썬의 람다 형태는 서술문을 포함할 수 없는데 파이썬의 구문적 프레임워크가 표현식안에 내포된 서술문을 처리할 수 없기 때문입니다.

그렇지만, 파이썬에서, 이것은 큰 문제가 아닙니다. 기능을 추가하는, 다른 언어에서의 람다 형태와는 다르게, 파이썬 람다는 너무 게을러서 함수를 정의할 수 없을때 사용하는 단축 표기법일 뿐입니다.

함수는 파이썬의 이미 첫번째 클래스이며, 지역 영역에서 선언될 수 있습니다. 그러므로 지역적으로-정의된 함수 대신에 람다형태를 사용하는 유일한 이점이라면 그 함수를 위한 이름을 만들 필요가 없다는 것입니다 -- 그러나 그것은 함수 객체가 (람다 형태가 산출하는 것과 정확하게 똑 같은 형태의 객체입니다) 할당된 지역 변수일 뿐입니다!


6.10. 왜 람다는 둘러싼 영역 안에 정의된 변수에는 접근하지 않나요?

왜냐하면 람다는 평범한 함수로 구현 되었기 때문입니다. 질문 4.5를 참조하세요.


6.11. 왜 재귀 함수는 다른 함수 안에 정의 될 수는 없나요?

위의 질문 4.5를 참조하세요. 그러나 실제로 재귀 함수는 약간의 꼼수를 사용하여 다른 함수 안에 정의될 수 있습니다.

    def test():
        class factorial:
             def __call__(self, n):
                 if n<=1: return 1
                 return n * self(n-1)
        return factorial()
    fact = test()
위의 factorial()에 의해서 생성된 실체는 재귀 계승 함수처럼 행동합니다.

상호호출적인 재귀 함수는 서로간에 인수로서 건네질 수 있습니다.


6.12. keys()의 리스트를 먼저 구성하는 것보다 사전을 반복하는 더 효율적인 방법은 왜 없나요?

한번 시도해 보셨습니까? 그것은 당신의 목적에 충분할 정도로 빠르다고 장담합니다! 대부분의 경우에 그러한 리스트는 그 사전이 점유한 공간의 아주 작은 부분만을 차지할 뿐입니다. 그 고정된 머리부를 제외하고, 그 리스트는 키 당 겨우 4바이트(포인터의 크기) 만을 사용합니다. 사전은 키당 12바이트를 차지하고 게다가 해쉬 테이블의 30에서 70퍼센트를 부담으로 더 사용합니다, 또 키와 값들을 위한 공간을 차지합니다. 필수적으로, 모든 키들은 구별되어야 하는 객체입니다, 그리고 (가장 일반적인 키의 형태인) 문자열 객체는 적어도 20바이트, 게다가 그 문자열의 길이만큼을 소비합니다. 거기에 사전에 담겨 있는 값들을 더해보면, 한 항목당 4 바이트는 실제로 그렇게 많은 메모리 소비가 아님을 알 수 있습니다...

dict.keys()를 호출하면 그 사전을 신속하게 한 번 훓어서 (내부적으로, 반복 함수가 존재합니다) 그 키 객체에 대한 참조점들을 미리-할당된 합당한 크기의 리스트 객체에 복사합니다. 반복 시간은 손해가 아닙니다 (왜냐하면, 어쨌든 반복해야만 할 것이기 때문입니다 -- 많은 경우에 여러분의 회돌이가 대단히 일찍 종료하지 않는 한 말이지요. (그 키를 무작위 순서로 얻을 것이기 때문에 대단히 일찍 종료하지는 않을 것이라고 생각합니다).

나는 그 사전 반복 연산을 파이썬 프로그래머들에게 공개하지 않고 있는데 왜냐하면 사전은 전체 반복 기간중에 변경되어서는 안되기 때문입니다 -- 만약 그렇게 되면, 그 사전이 재조직될 가능성이 약간 있기 때문인데 왜냐하면 해쉬 테이블이 너무 꽉 차서, 그 반복이 약간의 항목들을 놓치게 될 수 있고 다른 것들은 두번 처리할 가능성이 있기 때문입니다. 정확하게 말하면 이것은 아주 드둘게 일어나므로, 프로그램에 있는 버그를 숨기는 경향이 있습니다: 단지 반복할 때마다 작은 수의 항목을 삭제하거나 삽입하기만 할 뿐이라면 그것이 테스트중에 절대로 일어나지 않도록 하는 것은 쉬운 일입니다 -- 그러나 사용자들은 조만간 확실히 그것과 마주치게 될 것입니다.


6.13. 파이썬은 기계 코드, C 혹은 다른 언어로 컴파일 될 수 있나요?

쉽지는 않습니다. 파이썬의 고 수준 데이타 형, 객체에 대한 동적 형정의 그리고 인터프리터를 실행-시간에 호출 등은 모두 "컴파일된" 파이썬 프로그램이 거의 대부분, 심지어는 "x+1"과 같은 대단히 단순해 보이는 연산에 조차도 파이썬의 실행-시간 시스템에 대한 호출로 구성될 것이라는 것을 뜻합니다.

파이썬 뉴스그룹 혹은 지난 파이썬 회의에서 설명된 다수의 프로젝트를 보면, 지금까지 달성된 그 속도가(예. 2x) 겨우 참을 정도이긴 하지만, 이러한 접근이 가능해 보입니다. JPython 역시 자바 바이트코드로 컴파일하기 위하여 같은 전략을 사용합니다. (Jim Hugunin이 예시해준 바에 의하면 전체-프로그램 분석을 통하여 여러 조합을 해보면 작은 데모 프로그램에서는 1000x 속도 개선도 가능해 보입니다. 1997 파이썬 회의용 웹 사이트를 참조하세요.)

내부적으로, 파이썬 소스 코드는 ("파이썬 가상 기계" 혹은 "바이트코드 인터프리터"에 의하여) 번역되기 전에 항상 "가상 기계 코드" 또는 "바이트 코드" 표현으로 번역됩니다. 자주 변경되지는 않는 모듈을 해석하고 번역하는 부담을 피하기 위하여, 이 바이트 코드는 그 이름이 ".py"로 끝나는 파일로부터 모듈이 해석될 때마다 이름이 ".pyc"로 끝나는 파일에 작성됩니다. 그 상응하는 .py 파일이 변경되면, 다시 해석되고 번역되어서 .pyc 파일이 재작성됩니다.

일단 .pyc 파일이 적재 되면 성능차이는 없습니다. ( .pyc 파일에서 읽은 바이트 코드는 직접 번역에 의해서 생성된 바이트 코드와 정확하게 똑 같습니다). 유일한 차이점은 .pyc 파일을 적재하는 것이 .py 파일을 해석하고 번역하는 것보다 빠르다는 것입니다, 그래서 미리 컴파일된 .pyc 파일이 있다면 일반적으로 파이썬 스크립트를 기동-하는 시간을 개선시켜 줄 것입니다. 원하기만 한다면, Lib/compileall.py module/script를 사용해서 주어진 모듈 모음에 대하여 유효한 .pyc 파일들을 강제로 생성할 수 있습니다.

주목할 것은 파이썬에 의하여 실행되는 주 스크립트는, 그 파일 이름이 .py으로 끝남에도 불구하고, .pyc 파일로 컴파일되지 않는다는 것입니다. 그것은 바이트 코드로 컴파일됩니다, 그러나 그 바이트 코드는 파일로 저장되지 않습니다.

만약 인터프리터와 라이브러리를 배포할 필요없이, 이진파일 형태로 배포하기 위하여 파이썬 프로그램을 번역하는 방법을 찾고 계신다면, freeze.py 스크립트를 Tools/freeze 디렉토리에서 찾아 보세요. 이것으로 여러분의 프로그램, 파이썬 인터프리터와, 그리고 필요한 파이썬 라이브러리등을 한개의 이진 파일로 만들 수 있습니다. 물론, 결과로 나오는 이진 파일은 그것을 만드는데 사용했던, 같은 형태의 플랫폼에서 실행될 것입니다.


6.14. 어떻게 파이썬은 메모리를 관리하나요? 왜 완전 쓰레기 수집을 하지 않나요?

파이썬의 메모리 관리의 세부조건은 그 구현에 달려 있습니다. 표준 파이썬 구현( C 구현)은 참조 횟수 메모리 관리를 사용합니다. 이것은 한 객체가 더 이상 파이썬에서 사용되지 않을 때 그 객체는, 약간의 예외를 가지고 자동적으로 제거된다는 것을 뜻합니다.

반면에, JPython은 자바 실행시간 라이브러리에 기초합니다; 그래서 그것은 JVM 쓰레기 수집기를 사용합니다. 이 차이로 인하여 파이썬 코드가 참조 횟수 구현의 행위에 기초함에도 불구하고 약간 미묘한 이식 문제가 발생합니다.

표준 파이썬에 대하여 기억하여할 두 가지 예외는:

1) 만약 객체가 순환 참조 경로에 있다면 그 순환고리가 깨지지 않는 한 제거되지 않을 것입니다. 예:

       List = [None]
       List[0] = List
List는 그 순환고리(List[0]이 리스트입니다)가 끊어지지 않는 한 제거되지 않을 것입니다. 리스트가 제거되지 않는 이유는 그것이 접근할 수 없게 됨에도 불구하고 그 리스트는 그 자체에 참조를 가지고 있고, 참조 횟수 세기는 한 객체를 모든 참조가 파괴될 때에만 없애기 때문입니다. 순환 참조 경로를 끊으려면 그 참조를 다음과 같이 파괴해야 합니다.

       List[0] = None
그래서, 만약 프로그램이 순환 참조를 생성하면 (그리고 장시간 실행되고 혹은 많은 메모리를 소비한다면) 순환 구조를 명확하게 관리해야할 필요가 있습니다. 많은 어플리케이션 영역에서, 혹시 있을지는 모르지만, 이것은 극히 드물게 필요합니다.

CPython 2.0에서는 이 문제를 해결 했는데 주기적으로 순환 탐색 알고리즘을 실행시켜서 접근할 수 없는 순환을 찾아 관련된 객체들을 삭제합니다. 새로운 gc 모듈은 쓰레기 수집을 수행하고, 디버깅 정보를 획득하며, 그리고 그 쓰레기 수집기의 매개변수를 조율하는 함수들을 제공합니다.

그 순환 탐색 알고리즘을 수행하는 것은 약간 시간을 필요로 하므로, 결과적으로 약간의 부담이 있습니다. 파이썬 2.0에서 그 순환 수집을 경험해 본 후에, 파이썬 2.1은 그 부담을 조심스럽게 조율하여 최소화 할 수 있기를 바라고 있습니다. 아직까지는 얼마나 많은 성능이 저하되었는지 명백하지 않습니다, 왜냐하면 이것을 벤치마킹하는 것은 꼼수적이고 그 프로그램이 얼마나 자주 객체를 생성하고 파괴하는지에 절대적으로 의존하기 때문입니다. 순환을 탐지하는 것은 파이썬이 컴파일 될 때 사용하지 못하게 할 수 있습니다, 만약 약간의 속도 저하도 허용할 수 없으며 그 순환 수집이 문제가 있다고 의심되신다면, 설정 스크립트를 실행할 때에 "-without-cycle-gc" 스위치를 지정하세요.

2) 때로는 객체가 "역추적(tracebacks)"에 일시적으로 고착됩니다 그러므로 예상대로 제거되지 않습니다. 그 역추적을 다음과 같이 제거하세요

       import sys
       sys.exc_traceback = sys.last_traceback = None
역추적은 에러를 보고하고 디버거를 구현하고 등등의 관련된 것들을 위하여 사용됩니다. 역추적에는 예외(보통은 가장 최근의 예외)를 다루는 도중에 추출된 약간의 프로그램 정보가 저장됩니다.

순환 참조와 modulo 역추적이 없다면, 파이썬 프로그램은 명시적으로 메모리를 관리할 필요는 없습니다.

때로 제안 되는 것은 파이썬이 일반적인 완전한 쓰레기 수집으로 혜택을 볼 수 있다는 것인데, 그러면 더욱 더 파이썬은 "자동적인" 쓰레기 수집 (GC)을 획득할 가능성이 없게 되리라고 봅니다. 한 이유로, 이것이 C 에 표준 사양으로 추가되지 않는 한, 그것은 발등위에 떨어진 이식성을 위한 고통입니다. 네 물론, 저도 Xerox 라이브러리에 대하여 압니다. 그것은 가장 일반적인 플랫폼들을 위한 어셈블러 코드를 가집니다. 모든 것은 아닙니다. 그리고 그것이 대단히 투명하기는 하지만, 전적으로 투명한 것은 아닙니다 (한 때 파이썬을 그것과 연결하여 보니, 그것은 내부의 비밀을 쏟아내었습니다).

"적절한" GC는 또한 파이썬이 다른 어플리케이션에 내장되면 문제가 됩니다. 독-립 파이썬에서는 표준 malloc()과 free()를 GC 라이브러리가 제공한 버전으로 바꾸어도 괜찮은 반면에, 파이썬을 내장한 어플리케이션은 파이썬의 것이 아닌, 자신만의 malloc()과 free() 대체물을 가지기를 원할 수 있습니다. 지금 현재로서는, 파이썬은 malloc()과 free()를 적절하게 구현하는 어떤 것과도 잘 작동합니다.

쓰레기 수집을 가지고 있는, JPython에서, 다음의 코드는 (C 파이썬에서는 이상없음) 메모리를 다 소비하기 전에 아마도 파일 기술자를 다 소비하게 될 것입니다:

        for file in <very long list of files>:
                f = open(file)
                c = f.read(1)
현재의 참조 횟수 세기와 파괴 전략를 사용하면, f에 대해 새롭게 할당할 때 마다 그 전의 파일을 닫습니다. GC를 사용하면, 이것을 보증할 수 없습니다. 물론, 여러분은 이 문제를 해결하는 방법에 대해 생각해 볼 수 있습니다. 그러나 그것은 구형(off-the-shelf) 기술이 아닙니다. 만약 어떤 파이썬 구현과도 잘 작동할 코드를 작성하기를 원한다면, 명료하게 그 파일을 닫아야만 합니다; 이것은 GC와 상관없이 잘 작동할 것입니다:

       for file in <very long list of files>:
                f = open(file)
                c = f.read(1)
                f.close()

상술한 모든 것에도 불구하고, 어떤 사람이 GC를 파이썬에 Xerox의 GC 라이브러리를 이용하여 추가하였습니다, 그래서 몸소 찾아 보실 수 있습니다. 다음을 보세요

	http://starship.python.net/crew/gandalf/gc-ss.html
흔한 메모리 누수현상을 수동으로 찝어 내는 방법에 대한 것은 질문 4.17을 참조하세요.

여기에서의 답변에 만족하실 수 없다면, 뉴스그룹에 글을 올리기 전에, GC에 관한 지난 논의를 Moshe Zadka가 요약해 놓은 것을 읽어 보세요:

http://www.geocities.com/TheTropics/Island/2932/gcpy.html


6.15. 왜 별개로 터플과 리스트 데이타 형이 존재하나요?

터플은 변경불가능하고 반면에 리스트는 변경가능하도록 하기 위해 그렇습니다.

변경불가능한 퍼플은 약간의 항목들을 함수에 건넬 필요가 있고 그 함수가 건네진 터플을 변경하기를 원하지 않을 경우에 유용합니다; 예를 들어,

	point1 = (120, 140)
	point2 = (200, 300)
	record(point1, point2)
	draw(point1, point2)
여러분은 record()가 그 좌표들을 변경한다면 무슨일이 일어날지 생각할 필요가 있기를 원하지 않을 것입니다. -- 변경할 수는 없을 텐데, 왜냐하면 그 터플들은 변경불가능하기 때문입니다.

반면에, 거대한 리스트를 동적으로 만들때에는, 변경가능하다는 것이 절대적으로 중요합니다. -- 요소들을 터플에 하나씩 추가하는 것은 연결 연산자의 사용을 요구하고, 그것때문에 제곱시간이 소요됩니다.

일반적인 한계선으로서, 터플은 C에서 구조형 혹은 파스칼에서 레코드형을 사용하는 것처럼 사용하시고, 리스트는 (가변 길이) 배열 처럼 사용하세요.


6.16. 리스트는 어떻게 구현되었나요?

리스프 프로그래머들이 어떻게 생각하든지, 파이썬의 리스트는 실제로는 가변-길이 배열입니다. 그 구현은 다른 객체에 대한 연속적인 배열 참조를 사용하고, 그리고 이 배열에 대한 포인터를 (뿐만 아니라 그의 길이도) 리스트 머리 구조에 유지합니다.

이것으로 리스트를 지표화하는 연산의 비용은 지표의 값 혹은 리스트의 크기에 독립적이 됩니다.

항목들이 추가되거나 삽입되면, 그 참조 배열은 크기가 변합니다. 항목을 반복적으로 추가하는 성능을 개선하는데 약간의 지혜를 적용하였습니다; 그 배열이 커져야만 한다면, 여분의 공간이 할당되어 다음 몇 번동안은 실제적인 크기 변경을 요구하지 않게됩니다.


6.17. 어떻게 사전이 구현되었나요?

파이썬의 사전은 크기 변경가능한 해쉬 테이블로서 구현되었습니다.

B-트리와 비교하여, 이것은 대부분의 환경하에서 더 좋은 탐색(너무나도 가장 흔한 연산) 성능을 보여 주며, 그 구현이 더 간단합니다.


6.18. 왜 사전의 키는 변경불가한가요?

해쉬 테이블로 사전을 구현하였으므로 키 값으로부터 계산된 해쉬 값을 사용하여 키를 찾습니다. 만약 그 키가 변경가능한 객체라면, 그 값은 변경될 수 있습니다, 그리하여 그 해쉬값은 변경될 수 있습니다. 그러나 그 키 객체를 변경하는 자는 그것이 사전에 구체화되었는지를 구별할 수 없기 때문에, 그 엔트리를 그 사전에서 이리저리 옮길 수 없습니다. 그때, 같은 객체를 그 사전에서 참조하려고 하면, 발견되지 않을 것입니다, 왜냐하면 그 해쉬 값이 다르기 때문입니다; 그리고 이 전의 값을 찾아 보려고 하면, 역시 발견되지 않을 것입니다, 왜냐하면 해쉬 깡통에서 발견된 그 객체의 값이 다르기 때문입니다.

리스트로 색인된 사전이 필요하다고 생각하신다면, 대신에 터플을 사용하세요. 함수 tuple(l)은 리스트 l과 똑 같은 항목들을 가지는 터플입니다.

받아들일 수 없는 해결책들도 제시되었는데:

- 주소 값( 객체 신분번호(object ID))에 의한 해쉬 리스트. 같은 값으로 새로운 리스트를 구성하면 발견되지 않을 것이기 때문에 이것은 작동하지 않습니다; 예를 들어,

  d = {[1,2]: '12'}
  print d[[1,2]]
KeyError 예외를 일으킬 것입니다. 두번째 라인에서 사용된 [1,2]의 id가 첫 번째 라인의 id와 다르기 때문입니다. 다른 말로 하면, 사전의 키들은 'is'를 사용하지 말고, '=='를 사용하여 비교되어야만 합니다.

- 리스트를 키로 사용할 때는 복사본을 만들기. 이것은 작동하지 않습니다. 왜냐하면 그 리스트는 (변경가능한 객체이므로) 자신에 대한 참조점을 포함할 수 있고, 그리고 그 복사 코드는 실행되어 무한 회돌이에 빠질지도 모르기 때문입니다.

- 리스트를 키로 허용하지만 사용자가 그것들을 변경하지 못하도록 한다. 이것은 프로그램에서 추적-하기-어려운 버그를 가지는 클래스를 허용하는데 나는 이것을 용서할 수 없습니다; 그것은 사전의 중요한 상수를 무효하게 만듭니다. (d.keys()에 있는 모든 값은 그 사전의 키로 사용될 수 있습니다).

- 리스트가 사전의 키로 사용되면 읽기-전용으로 표시해 놓기. 문제는 단지 최상위-객체 만이 그 값을 변경할 수 있는 것은 아니라는 것입니다; 리스트를 담고 있는 터플을 키로 사용할 수 있습니다. 무언가를 사전에 키로 입력하려면 거기로부터 도달할 수 있는 모든 객체를 읽기-전용으로 표시해야할 필요가 있을 것입니다 -- 그리고 또, 자기-참조적인 객체는 무한 회돌이를 다시 (또 다시 계속하여) 야기할 수 있습니다.

필요하다면 이것을 피하는 꼼수가 있습니다, 그러나 여러분의 책임하에 그것을 사용하세요: 변경가능 구조를 __cmp__와 __hash__ 메쏘드 둘 다를 가지는 클래스 실체 안에 포장해 넣을 수 있습니다.

   class listwrapper:
        def __init__(self, the_list):
              self.the_list = the_list
        def __cmp__(self, other):
              return self.the_list == other.the_list
        def __hash__(self):
              l = self.the_list
              result = 98767 - len(l)*555
              for i in range(len(l)):
                   try:
                        result = result + (hash(l[i]) % 9999999) * 1001 + i
                   except:
                        result = (result % 7777777) + i * 333
              return result
주목할 것은 그 리스트의 어떤 구성원들은 해쉬될 수 없을 수도 있다는 가능성 그리고 또 산술적인 범람의 가능성으로 인하여 해쉬 계산이 복잡하다는 것입니다.

반드시 확인해야 하는 것은, 한 사전(또는 해쉬에 기초한 다른 구조)에 존재하는 그러한 모든 포장 객체를 위한 해쉬 값은, 그 객체가 그 사전(또는 다른 구조)에 있는 동안에는, 고정되어 있어야 한다는 것입니다.

게다가 항상 다음과 같이 만약 o1 == o2 (즉 o1.__cmp__(o2)==0)이라면, 그 객체가 사전에 있는지 없는지에 상관없이, hash(o1)==hash(o2) (즉, o1.__hash__() == o2.__hash__())이어야 합니다. 이러한 제한사항들을 만족시키지 못한다면 사전과 해쉬에 기초한 다른 구조들은 엉뚱하게 행동할지도 모릅니다!

상술한 리스트포장자의 경우에 있어서 그 포장 객체가 사전에 존재할 때마다 그 포장된 리스트는 이상증상을 피하기 위하여 변경되어서는 안됩니다. 그것들이 정확하게 일치되지 않을 때의 결과와 그 필요성에 관하여 심각하게 재차 고려해보지 않는한 이렇게 하지 마세요. 경고 받습니다!


6.19. 도데체 어떻게 파이썬에서 배열을 작성하나요?

["this", 1, "is", "an", "array"]

리스트는 C 나 파스칼 세계의 관점에서 배열입니다 (질문 6.16 참조). 배열 모듈은 또한 고정 형의 배열을 간결한 표현으로 생성하기 위한 메쏘드들을 제공합니다 (그러나 그것들은 인덱스에 대하여 리스트보다 느립니다). 또 주목할 것은 Numerics 확장과 다른 확장들은 다양한 특징 역시 가지는 유사-배열 구조를 정의한다는 것입니다.

리스프-류의 리스트를 획득하고, cons cells를 흉내내려면

    lisp_list = ("like",  ("this",  ("example", None) ) )
터플을 사용하고 (혹은 변경가능을 원하면, 리스트를 사용하세요). 여기에서 lisp의 car과 유사한 것은 lisp_list[0]이고 cdr과 유사한 것은 lisp_list[1]입니다. 실제로 필요하다고 확신할 때만 이렇게 하세요 (그것은 보통 파이썬의 리스트를 사용하는 것보다 많이 느립니다).

파이썬 객체를 담은 변경가능한 이질적인 배열로서 파이썬의 리스트를 고려하세요 (예를 들어 10 배 빠릅니다 :) ).


6.20. 왜 list.sort()가 정렬된 리스트를 반환하지 않나요?

수행속도가 문제가 되는 상황에서, 단지 정렬하기 위해 그 리스트를 복사한다는 것은 낭비가 될 것입니다. 그러므로, list.sort() 는 그 리스트를 적절하게 정렬합니다. 그것은, 그 사실을 여러분에게 알려 주기 위하여, 그 정렬된 리스트를 반환하지는 않습니다. 이러한 방식 때문에, 정렬된 복사본이 필요하고, 정렬되지 않은 버전 역시 유지할 필요가 있을 때, 착각해서 우연하게 리스트를 덮어쓰지는 않게 될 것입니다.

결과적으로, 정렬된 순서로 존재하는 사전의 키들에 관하여 여기에 반복해야할 관용구가 있습니다:

	keys = dict.keys()
	keys.sort()
	for key in keys:
		...do whatever with dict[key]...


6.21. 파이썬에서 어떻게 인터페이스 사양을 지정하고 강제하나요?

C++이나 자바같은 언어들이 제공하는 모듈을 위한 인터페이스 사양은 그 모듈의 함수와 메쏘드에 대한 프로토타입을 기술합니다. 많은 사람들이 컴파일 시간에 인터페이스 사양을 강제하는 것이 거대한 프로그램을 만들 때에 도움이 된다고 느낍니다. 파이썬은 인터페이스 지정을 직접적으로 지원하지 않습니다, 그러나 컴포넌트를 위한 적절한 테스트 규율로 많은 이점들을 얻을 수 있는데, 파이썬에서는 쉽게 달성될 수 있습니다.

모듈을 위한 훌륭한 테스트 모둠은 회귀 테스트를 제공할 수 있을 뿐만 아니라 모듈 인터페이스 사양으로 기능할 수도 있습니다 (더 좋은 것은 그것이 예제 사용법을 제공하기도 하기 때문입니다). 많은 표준 라이브러리를 살펴 보면 때로는 "스크립트 번역(script interpretation)"을 가지고 있는데 간단한 "자기 테스트(self test)"를 제공하는 것을 볼 수 있습니다. 복잡한 외부 인터페이스를 사용하는 모듈조차도 때로는 그 외부 인터페이스에 대한, 아주 작은 "그루터기(stub)-임시 테스트 모듈" 흉내를 사용하여 독립적으로 테스트될 수 있습니다.

적절한 테스트 규율은 (강제 되면) 파이썬으로 거대하고 복잡한 어플리케이션을 구축하는 것을 도울 수 있습니다 뿐만 아니라 인터페이스 사양을 가지는 것도 그럴 것입니다 (또는 더 좋을 수도있습니다). 물론 파이썬에 여러분은 나약해지고 그렇게 하지 못 합니다. 또한 쉽게 테스트하기 위하여 눈으로 코드를 디자인하기를 원할 수도 있습니다.


6.22. 왜 모든 클래스는 같은 형을 가지나요? 왜 실체들은 모두 같은 형을 가지나요?

"형(type)"이라는 단어를 파이썬적으로 사용하는 것은 다른 많은 프로그래밍 언어의 세계에서의 일반적인 사용법과 아주 많이 다릅니다. 파이썬에서 "type"은 한 객체의 연산에 대한 설명으로서 그 연산은 C로 구현됩니다. 모든 클래스는, 때로는 파이썬으로 구현된 다른 프로그램 조각을 "역 호출하는", C 로 구현된, 똑 같은 연산을 가집니다, 그러므로 모든 클래스는 같은 형을 가집니다. 비슷하게 C 수준에서 모든 클래스 실체는 같은 C 구현을 가집니다, 그러므로 모든 실체들은 같은 형을 가집니다.

기억할 것은 파이썬에서의 "type" 사용이 한 객체의 C 구현을 참조한다는 것입니다. 서로 다른 클래스의 실체들 사이를 구별하려면 Instance.__class__를 사용하시고, 또 4.47을 참조하세요. 용어상의 혼동에 대하여 유감입니다만, 파이썬의 발전 단계의 이 시점에서는, 뭐라고 말할 수 없습니다!


6.23. 왜 파이썬이 종료할 때 모든 메모리가 풀어지지 않나요?

파이썬 모듈의 전역 이름 공간으로부터 참조된 객체들은 파이썬이 종료할 때에 항상 할당이 취소되지 않습니다.

이것은 순환 참조가 있을 경우에(질문 4.17 참조) 일어납니다. 풀기 불가능한 C 라이브러리에 의하여 할당된 메모리도 있습니다 (예를 들어 Purify와 같은 도구는 이것에 관하여 불평을 할 것입니다).

그러나 일반적으로, 파이썬 1.5 이상에서는 (이전의 버전과 대조적으로) 종료시에 대단히 공격적으로 메모리를 청소합니다.

강제적으로 파이썬이 할당 취소시에 어떤 것들을 삭제하도록 하기를 원한다면, sys.exitfunc 낚아채기를 사용하여 그러한 삭제를 강제하세요. 예를 들어 만약 메모리 분석 도구를 사용하여 확장 모듈을 디버깅하고 있고 파이썬이 거의 모든 것들을 할당취소하도록 강제하기를 원한다면 다음과 같은 exitfunc를 사용할 수 있습니다:

  import sys
  def my_exitfunc():
       print "cleaning up"
       import sys
       # do order dependant deletions here
       ...
       # now delete everything else in arbitrary order
       for x in sys.modules.values():
            d = x.__dict__
            for name in d.keys():
                 del d[name]
  sys.exitfunc = my_exitfunc
물론, 다른 exitfuncs는 덜 강렬할 수 있습니다.

(사실, 이 함수는 파이썬이 현재 이미 자체적으로 하고 있는 것을 수행하고 있습니다; 그러나 sys.exitfunc를 사용하여 강제로 청소를 하는 예제는 여전히 유용합니다.)


6.24. 왜 클래스 메쏘드 또는 클래스 변수가 없습니까?*

다음과 같은 표기법은

    instance.attribute(arg1, arg2)
보통 다음과 같이 동등하게 번역됩니다

    Class.attribute(instance, arg1, arg2)
여기에서 Class는 실체의 (상위)클래스 입니다. 비슷하게 다음은

    instance.attribute = value
실체의 속성을 설정합니다 (실체가 상속받은 클래스의 어떤 속성이라도 덮어써서).

가끔 프로그래머들은 다른 행위를 가지기를 원합니다 -- 실체에 엮여있지 않는 메쏘드 그리고 적절하게 변경되는 클래스 속성을 원합니다. 파이썬은 이러한 행위들을 막지 않습니다, 그러나 관례적으로 그것들을 구현할 필요가 있습니다. 이것을 달성하는 한 가지 방법은 "리스트 포장자(list wrappers)"와 전역 함수를 사용하는 것입니다.

   def C_hello():
         print "hello"
   class C:
        hello = [C_hello]
        counter = [0]
    I = C()
여기에서 I.hello[0]()는 "class method"와 아주 똑 같이 행동합니다 그리고 I.counter[0] = 2 는 C.counter를 변경합니다 (그리고 그것을 덮어쓰지 않습니다). 도대체 왜 이렇게 해야 하는지를 이해 못하겠다면, 그것은 여러분이 순수하기 때문입니다, 그리고 아마도 절대로 그렇게 하기를 원하지 않을 것입니다! 이것은 위험한 꼼수입니다, 가능하다면 추천하지 않습니다. (Tim Peter의 토론에서 영감을 얻음.)

파이썬 2.2에서는, 새로이 내장된 연산인 classmethod와 staticmethod를 사용하여 이렇게 할 수 있습니다. 다음을 참조: http://www.python.org/2.2/descrintro.html#staticmethods


6.25. 왜 기본 값들이 때로는 객체들 사이에 공유되나요?

함수 호출을 하면 기본 값에 대하여 새로운 객체가 만들어 진다고 자주 예상하지만, 실제로 이것이 일어나는 것이 아닙니다. 기본 값들은 함수가 정의될 때 작성됩니다, 즉, 모든 함수들이 참조하는 그러한 오직 하나의 객체 만이 있습니다. 만약 그 객체가 변경된다면, 그 함수에 대한 잇다르는 호출들은 이 변경된 객체를 참조할 것입니다. 정의상으로, (숫자, 문자열, 터플, None과 같은) 변경 불가 객체는 변경으로부터 안전합니다. (사전, 리스트, 클래스 실체와 같은) 변경가능 객체에 대한 변경이 혼동을 야기하는 것입니다.

이러한 사양 때문에 변경가능 객체를 기본 값으로 사용하지 않고, 오히려 그것들을 그 함수에 도입하는 것이 좋은 프로그래밍 관습입니다. 다음과 같이 작성하지 마세요:

	def foo(dict={}):  # XXX shared reference to one dict for all calls
	    ...
그러나:
	def foo(dict=None):
		if dict is None:
			dict = {} # create a new dict for local namespace
이러한 사양에 대한 논의를 보려면 "파이썬으로 하는 인터넷 프로그래밍(Internet Programming with Python)"의 182 페이지를 보세요. 혹은 또 다른 논의를 보려면 "파이썬 프로그래밍하기(Programming Python)"의 144 페이지 상단 또는 277 페이지 하단을 보세요.


6.26. 왜 no goto가 없는가?

실제로, 예외를 사용하여 "구조적인 goto"를 제공할 수 있는데 심지어는 함수 호출에도 작동합니다. 많은 사람들은 C, 포트란, 그리고 다른 언어들이 충분한 이유를 가지고 "go" 혹은 "goto" 구조를 사용하는 모든 곳에 대하여 예외로 편리하게 흉내낼수 있다고 느낍니다. 예를 들어:

   class label: pass # declare a label
   try:
        ...
        if (condition): raise label() # goto label
        ...
   except label: # where to goto
        pass
   ...
이것으로 회돌의 중앙에 뛰어 들 수는 없습니다, 그러나 보통 그것은 어쨋든 goto의 남용이라고 간주됩니다. 아껴서 사용하세요.


6.27. 파이썬에서 더 높은 순위의 함수를 어떻게 작성하나요?

두 개의 선택이 있습니다: 기본 인수들을 사용해서 그것들을 덮어 쓸 수 있거나 혹은 "호출 가능 객체 (callable objects)."를 사용할 수 있습니다. 예를 들어 linear(a,b)를 정의해서 함수 f를 반환시키기를 원한다고 가정합시다. 거기에서 f(x)는 그 값 a*x+b를 계산합니다.

기본 인수들을 사용하기:

     def linear(a,b):
         def result(x, a=a, b=b):
             return a*x + b
         return result
혹은 호출가능 객체를 사용하기:

     class linear:
        def __init__(self, a, b):
            self.a, self.b = a,b
        def __call__(self, x):
            return self.a * x + self.b
둘 모두의 경우에:

     taxes = linear(0.3,2)
라고 하면 호출가능한 객체를 얻는데 여기에서 taxes(10e6) == 0.3 * 10e6 + 2 입니다.

기본 값 전략은 약점이 있는데 기본 인수들이 우연하게 혹은 악의적으로 덮어씌여질 수 있습니다. 호출가능 객체 접근법의 약점은 약간 느리고 약간 더 길다는 것입니다. 그렇지만 주목할 것은 호출가능 객체들의 모임이 상속을 통하여 인증을 공유한다는 것입니다. 예를 들어:

      class exponential(linear):
         # __init__ inherited
         def __call__(self, x):
             return self.a * (x ** self.b)
comp.lang.python에서, zenin@bawdycaste.org이 지적한 바에 의하면 객체는 다수의 메쏘드들을 위한 상태정보를 캡슐화 할 수 있어서 기능적 프로그래밍 언어에서 빌려온 "울타리(closure)"의 개념을 흉내낼 수 있습니다, 예를 들어:

    class counter:
        value = 0
        def set(self, x): self.value = x
        def up(self): self.value=self.value+1
        def down(self): self.value=self.value-1
    count = counter()
    inc, dec, reset = count.up, count.down, count.set
여기에서 inc, dec 그리고 reset은 (그런 식으로 생각하고자 한다면) "변수 count.value를 담고 있는, 같은 울타리를 공유하는 함수처럼 행동합니다".


6.28. 'try' 안에서 'continue'에 대하여 왜 구문에러를 맞이 하나요?

이것은 구현의 한계인데, 바이트 코드를 생성하는 파이썬의 너무나 간결한-사고 방식으로 야기됩니다. try 블록은 무언가를 "블록 스택(block stack)"에 밀어 넣는데 continue문은 다시 그것을 끌어 내야 할 것입니다. 현재의 코드 생성기는 그 데이타 구조를 가지지 않으므로 'continue'가 올바른 코드를 만들어 낼 수 있습니다.

주목할 것은 JPython은 이러한 제한을 가지지 않는다는 것입니다!


6.29. 왜 미가공 문자열 (r-strings)은 역사선으로 끝날 수 없나요?

더 정확히 이야기 하면, 미가공 문자열은 홀수개의 역사선으로 끝날 수 없습니다: 맨 끝에 홀로 역사선이 남으면 닫음 부호 문자가 무시되어, 끝나지 않은 문자열을 남깁니다.

자신만의 역사선 탈출 처리를 가지고자 하는 처리기(주로 정규 표현식 엔진)에 대하여, 입력을 쉽게 생성할 수 있도록 미가공 문자열은 디자인 되었습니다. 어쨌든 그러한 처리기는 후미의 짝이 없는 역사선을 에러라고 간주합니다, 그래서 미가공 문자열은 그것을 허용하지 않습니다. 보상으로, 여러분은 역사선으로 탈출시킴으로써, 그 문자열에 인용 문자를 넘겨 줄 수 있습니다. 이러한 규칙은 미가공 문자열(r-strings)이 의도한 목적에 사용될 때는 잘 작동합니다 .

만약 윈도우에서 경로이름을 구축하고자 한다면, 주목할 것은 모든 윈도우 시스템 호출은 정사선도 역시 받아들인다는 것입니다:

    f = open("/mydir/file.txt") # works fine!
도스 명령어에 대한 경로 이름을 구축하고자 한다면, 예를 들어 다음 중의 하나를 시도해 보세요

    dir = r"\this\is\my\dos\dir" "\\"
    dir = r"\this\is\my\dos\dir\ "[:-1]
    dir = "\\this\\is\\my\\dos\\dir\\"


6.30. 왜 표현식에서 할당을 사용할 수 없나요?

많은 사람들이 C 나 펄의 불평에 익숙한데, 그들은 예를 들어 이러한 C 관용구를 사용할수 있기를 원합니다:

    while (line = readline(f)) {
        ...do something with line...
    }
이것에 대하여 파이썬으로는 이렇게 작성되어야 합니다:

    while 1:
        line = f.readline()
        if not line:
            break
        ...do something with line...
이 문제는 파이썬 뉴스 그룹에 놀라울 정도로 자주 올라옵니다 -- 데자 뉴스를 탐색하여 할당 표현식에 대한 지난 메시지들을 찾아보세요. 파이썬 표현식에서 할당이 허용되지 않는 이유는 다른 언어에서 이 구조에 의해서 야기되는, 자주있는, 찾기-너무-어려운 버그 때문입니다:

    if (x = 0) {
        ...error handling...
    }
    else {
        ...code that only works for nonzero x...
    }
많은 대안들이 제안되었습니다. 대부분은 해킹 수준으로 약간의 타자 수고를 절약해주기는 하지만 임의적이고 비밀스런 구문 혹은 키워드를 사용하기 때문에, 언어 변경 제안에 대하여 내가 사용한, 간결한 기준에 맞지 않습니다: 그 기준은 아직까지 그러한 구조를 접해 본적이 없는 인간 독자에게 적절한 의미를 제공해야만 합니다.

이것에 대하여 무언가를 할 수 있다면 신속하게 파이썬 2.0에 구현될 것입니다. -- 고쳐야만 할 가치가 있다면. 재미있는 현상은 대부분의 경력 파이썬 프로그래머들은 "while 1" 관용구를 인식하고 있고 표현 구조에서 그렇게 많이 할당을 빼먹는 것 같지는 않다는 것입니다; 오로지 초보자만이 이것을 언어에 추가하려는 강렬한 욕망을 표출합니다.

하나의 아주 우아한 해결책이라면 ":="로 철자되는, 표현식에서의 할당을 위한 새로운 연산자를 도입하는 것일 겁니다. -- 이것은 "=="문제가 아니라 "=" 문제를 피하기 위한 것입니다. this avoids the "=" instead of "==" problem. 그것은 비교 연산자로서 같은 우선 순위를 가질 것입니다 그러나 해석기는 다른 비교 연산자와의 조합을 (괄호를 고려도 하지 않고) 에러로 선언할 것입니다.

마지막으로 -- 이것을 철자한다는 대안적인 방식은 매력적으로 보이지만 일반적으로 "while 1" 해결책보다는 덜 튼튼합니다:

    line = f.readline()
    while line:
        ...do something with line...
        line = f.readline()
이것에 관한 문제는, 만약 정확하게 어떻게 다음 라인을 얻는지에 관하여 생각을 바꾼다면 (예를 들어. 그것을 sys.stdin.readline() 안으로 변경하기를 원한다면) 프로그램에서 두 군데를 변경해야 한다는 것을 기억할 필요가 있습니다 -- 두번째는 그 회돌이의 아래부분에 숨어 있습니다.


6.31. 왜 파이썬은 다른 언어처럼 "with" 서술문을 가지지 않나요?

기본적으로, 그러한 구조는 너무나 모호하게 될 지도 모르기 때문입니다. 다음의 의견에 대하여 Carlos Ribeiro씨에게 감사합니다:

객체 파스칼, 델파이, 그리고 C++ 같은, 어떤 언어들은 정적 형을 사용합니다. 그래서 어떤 구성원이 "with"절에서 할당되었는지를 명료하게 알 수 있습니다. 이것이 주요 논점입니다 - 그 컴파일러는 항상 컴파일 시간에 각 변수들의 영역을 알고 있습니다.

파이썬은 동적 형을 사용합니다. 어떤 속성들이 실행시간에 참조될지를 미리 아는 것은 불가능합니다. 구성원 속성들은 추가될 수도 있고 혹은 객체로부터 분주하게 제거 될 수도 있습니다. 이런 점 때문에, 단순한 읽기로는, 어떤 속성이 참조되고 있는지 - 지역 속성, 전역 속성, 혹은 구성원 속성인지를 알 수 없을 것입니다.

예를 들어, 다음의 조각코드를 예로 들어 봅시다 (그런데 이것은 미완성이며, 단지 그 아이디어를 보여주기 위한 것입니다):

   def with_is_broken(a):
      with a:
         print x
조각코드는 "a"가 "x"라고 부르는 구성원 속성을 가져야만 한다고 가정합니다. 그렇지만, 파이썬에서는 아무것도 그것을 보장하지 않습니다. 만약 "a"가, 예를 들어, 정수라면 무슨일이 일어날 것인가? 그리고 "x"라는 이름의 전역 변수를 가지고 있다면, 그것은 블록 안에서 사용되는 것으로 끝날 것인가? 보시다시피, 파이썬의 동적 특성은 그러한 선택을 더욱 어렵게 하고 있습니다.


앞으로    목차    1    2    3    4    5    제6장    7    8    다음으로