Python 2.2의 새로운 기능¶
- 저자:
A.M. Kuchling
소개¶
이 문서는 2002년 10월 14일에 출시된 Python 2.2.2의 새로운 기능을 설명합니다. Python 2.2.2는 원래 2001년 12월 21일에 출시된 Python 2.2 버그 수정 버전입니다.
Python 2.2는 “정리(cleanup) 릴리스”로 생각할 수 있습니다. 제너레이터나 이터레이터와 같이 완전히 새로운 기능들도 있지만, 가장 중요한 변화들은 언어 설계의 불규칙성과 어두운 구석을 정리하는 것을 목표로 합니다.
이 문서는 새로운 기능에 대한 완전한 명세(specification)를 제공하려는 것이 아니라 편리하게 개괄적인 설명을 제공하는 것입니다. 전체 세부 정보는 Python 2.2의 공식 문서, 예를 들어 `Python Library Reference <https://docs.python.org/2.2/lib/lib.html>`_와 `Python Reference Manual <https://docs.python.org/2.2/ref/ref.html>`_를 참조해야 합니다. 변경 사항에 대한 전체 구현 및 디자인 근거를 이해하고 싶다면, 특정 새로운 기능에 대한 PEP 문서를 참고하세요. 다만, PEP 문서는 일반적으로 기능이 완전히 구현된 이후에는 최신 상태로 유지되지 않을 수 있음에 유의하시기 바랍니다. 이전 버전의 파이썬에서 포팅하는 방법에 대한 지침은 `Porting to Python 3.13`_를 참조해 주십시오.
PEP 252와 253: 타입 및 클래스 변경 사항¶
Python 2.2에서 가장 크고 광범위한 변경 사항은 Python의 객체 및 클래스 모델에 있습니다. 이 변화들은 하위 호환성이 이루어져야 하므로, 코드가 변하지 않고 계속 실행될 가능성이 높지만, 이러한 변화는 놀라운 새로운 기능들을 제공합니다. 이 부분을 시작하기 전에, 본 문서에서 가장 길고 복잡한 섹션인 제가 변경 사항 개요를 제공하고 몇 가지 주석을 달도록 하겠습니다.
오래전 내가 파이썬 설계 결함을 나열한 웹 페이지를 작성했습니다. 가장 중요한 결함 중 하나는 C로 구현된 파이썬 타입의 서브클래싱이 불가능하다는 것이었습니다. 특히, 내장 타입을 서브클래싱 할 수 없으므로 리스트에 유용한 단일 메서드를 추가하기 위해 단순히 리스트를 서브클래스할 수 없습니다. UserList 모듈은 리스트의 모든 메서드를 지원하며 추가로 서브클래싱할 수 있는 클래스를 제공하지만, 순수 파이썬 리스트를 예상하고 일반적인 Python list로 처리하는 많은 C 코드가 존재하여 UserList 인스턴스를 받아들이지 않습니다.
Python 2.2에서 이 문제가 해결되었으며, 그 과정에서 몇 가지 흥미로운 새로운 기능이 추가되었습니다. 간략한 요약:
리스트와 같은 내장 타입을 심지어 정수까지 서브클래싱 할 수 있으며, 서브클래스는 원래 타입이 요구되는 모든 곳에서 작동해야 합니다.
이제 인스턴스 메서드 외에도 클래스와 스태틱 메서드를 정의하는 것이 가능합니다.
:dfn:`속성(properties)`를 사용하면 인스턴스 속성에 접근하거나 설정할 때 메서드를 자동으로 호출하는 것이 가능합니다. :meth:`~object.__getattr__`의 많은 사용 사례는 프로퍼티를 사용하도록 다시 작성될 수 있으며, 이를 통해 결과 코드가 더 단순하고 빨라집니다. 부수적인 이점으로는, 이제 어트리뷰트에 문서 문자열(docstrings)도 가질 수 있게 되었습니다.
:dfn:`슬롯(slots)`을 사용하면 인스턴스의 유효 속성 목록을 특정 집합으로 제한할 수 있습니다. 이는 오타를 방지하고, 향후 파이썬 버전에서 더 많은 최적화를 가능하게 할 수 있습니다.
일부 사용자들은 이러한 모든 변경 사항에 대해 우려를 제기해 왔습니다. 그들은 새 기능들이 멋지고 이전 버전의 파이썬에서는 불가능했던 모든 종류의 트릭에 사용될 수 있다면서도, 언어 자체를 더 복잡하게 만든다고 말합니다. 일부 사람들은 항상 파이썬의 단순함을 추천했다고 말하며, 그 단순함이 사라지고 있다고 느낍니다.
제 개인적인 생각으로는 걱정할 필요가 없습니다. 새 기능 중 상당수는 매우 전문적이며, 이러한 기능을 인식하지 않고도 많은 파이썬 코드를 작성할 수 있습니다. 간단한 클래스를 작성하는 것은 예전보다 더 어렵지 않으므로, 실제로 필요하지 않는 한 학습하거나 가르칠 필요가 없습니다. 이전에 C 언어에서만 가능했던 매우 복잡한 작업들이 이제 순수 파이썬으로도 가능해질 것이며, 제 생각에는 이것 모두 좋은 일입니다.
저는 새로운 기능들을 작동시키는 데 필요한 모든 사소한 코너 케이스와 변경 사항을 다루려고 노력하지는 않을 것입니다. 대신 이 섹션에서는 큰 틀만 보여드릴 것입니다. Python 2.2의 새로운 객체 모델에 대한 더 많은 정보는 관련 링크 “관련 링크” 섹션을 참조하십시오.
클래스 (Classes)¶
먼저 파이썬 2.2에는 실제로 두 가지 유형의 클래스가 있다는 것을 아셔야 합니다: 고전적인(classic) 또는 구형 스타일 클래스, 그리고 새로운 스타일 클래스입니다. 구형 스타일 클래스 모델은 파이썬 이전 버전의 클래스 모델과 완전히 동일합니다. 이 섹션에서 설명하는 모든 새 기능은 새로운 스타일 클래스에만 적용됩니다. 이러한 차이는 영원히 지속될 의도는 아니며, 궁극적으로는 Python 3.0에서 고형식 스타일 클래스가 제거될 가능성이 있습니다.
새 스타일 클래스를 정의하려면 어떻게 해야 할까요? 기존의 새로운 스타일 클래스를 상속받아 정의합니다. 정수(integers), 리스트, 딕셔너리, 심지어 파일과 같은 파이썬의 대부분 내장 타입들이 이제는 새로운 스타일 클래스입니다. 모든 내장 타입의 기본 클래스가 되는 :class:`object`라는 이름의 새로운 스타일 클래스도 추가되었으므로, 적합한 내장 타입이 없다면 단순히 :class:`object`를 상속받을 수 있습니다:
class C(object):
def __init__ (self):
...
...
이는 베이스 클래스가 없는 class 구문은 항상 Python 2.2에서 고전적인(classic) 클래스라는 것을 의미합니다. (실제로 모듈 수준 변수인 :attr:`!__metaclass__`를 설정하여 변경할 수도 있습니다 — 자세한 내용은 :pep:`253`을 참조하세요 — 하지만 단순히 :class:`object`를 상속받는 것이 더 쉽습니다.)
내장 타입의 타입 객체들은 교묘한 트릭을 사용하여 내장되어 사용할 수 있습니다. 파이썬은 항상 int(), float(), 및 :func:`str`이라는 이름의 내장 함수를 가지고 있었습니다. 2.2에서 이들은 더 이상 함수가 아니라, 호출될 때 팩토리 역할을 하는 타입 객체입니다:
>>> int
<type 'int'>
>>> int('123')
123
타입 세트를 완성하기 위해 dict() 및 file`과 같은 새로운 타입 객체가 추가되었습니다. 여기 더 흥미로운 예제가 있습니다: 파일 객체에 :meth:()!lock` 메서드를 추가하는 것:
class LockableFile(file):
def lock (self, operation, length=0, start=0, whence=0):
import fcntl
return fcntl.lockf(self.fileno(), operation,
length, start, whence)
이제 구식이 된 posixfile 모듈에는 모든 파일 객체의 메서드를 에뮬레이션하고 또한 lock() 메서드도 추가한 클래스가 포함되어 있었지만, 이 클래스는 내장 파일을 예상하는 내부 함수에 전달될 수 없었습니다. 이는 새로운 :class:`!LockableFile`로 가능해졌습니다.
디스크립터 (Descriptors)¶
이전 버전의 파이썬에는 어떤 객체가 어떤 어트리뷰트와 메서드를 지원하는지 일관되게 알아내는 방법이 없었습니다. __members__`나 :attr:!__methods__`처럼 이름 리스트를 정의하는 등의 비공식적인 관례들이 있었지만, 종종 확장 타입이나 클래스의 작성자가 이를 정의하기 귀찮아했습니다. 객체의 __dict__`를 검사하여 대체할 수 있었으나, 클래스 상속이나 임의의 :meth:!__getattr__` 훅이 사용되는 경우에도 정확하지 않을 수 있었습니다.
새로운 클래스 모델의 기본 아이디어는 객체의 어트리뷰트를 설명하기 위한 API가 :dfn:`디스크립터(descriptors)`를 사용하여 공식화되었다는 것입니다. 디스크립터는 어트리뷰트의 값을 지정하며, 그것이 메서드인지 필드인지를 명시합니다. 디스크립터 API 덕분에 정적 메서드와 클래스 메서드가 가능해졌으며, 더 특수한 구조들도 생겨났습니다.
어트리뷰트 디스크립터는 클래스 객체 내에 존재하는 객체이며 자체적으로 몇 가지 어트리뷰트를 가지고 있습니다:
:attr:`~definition.__name__`은 어트리뷰트의 이름입니다.
:attr:`~definition.__doc__`는 어트리뷰트의 문서 문자열(docstring)입니다.
__get__(object)은 object 로부터 어트리뷰트 값을 검색하는 메서드입니다.__set__(object, value)는 object 에 value 어트리뷰트를 설정합니다.__delete__(object, value)는 object 의 value 어트리뷰트를 삭제합니다.
예를 들어, obj.x 를 작성할 때 파이썬이 실제로 수행하는 단계는 다음과 같습니다:
descriptor = obj.__class__.x
descriptor.__get__(obj)
메서드의 경우, :meth:`descriptor.__get__ <object.__get__>`은 호출 가능한 임시 객체를 반환하며, 인스턴스와 해당 메서드를 묶어서 사용됩니다. 이것이 정적 및 클래스 메서드가 이제 가능한 이유이기도 합니다. 이 새로운 종류의 메서드에 대한 간략한 설명으로, 정적 메서드는 인스턴스를 전달받지 않으므로 일반 함수와 유사합니다. 클래스 메서드는 객체 자체가 아닌 해당 객체의 클래스를 전달받습니다. 정적 및 클래스 메서드는 다음과 같이 정의됩니다:
class C(object):
def f(arg1, arg2):
...
f = staticmethod(f)
def g(cls, arg1, arg2):
...
g = classmethod(g)
staticmethod() 함수는 함수 f`를 받아서, 이를 디스크립터로 래핑하여 클래스 객체에 저장할 수 있도록 합니다. 이러한 메서드를 생성하기 위한 특별한 구문(`def static f(), defstatic f(), 또는 이와 유사한 것)이 있을 것으로 예상할 수 있지만, 아직 그러한 구문은 정의되지 않았습니다. 이는 파이썬의 미래 버전을 위해 남겨진 것입니다.
슬롯(slots) 및 프로퍼티와 같은 더 많은 새로운 기능들도 새로운 종류의 디스크립터로 구현되었으며, 새롭고 독창적인 기능을 수행하는 디스크립터 클래스를 작성하는 것은 어렵지 않습니다. 예를 들어, 메서드에 대해 에이펠 스타일의 전제 조건과 사후 조건을 작성할 수 있게 해주는 디스크립터 클래스를 작성하는 것이 가능할 것입니다. 이 기능을 사용하는 클래스는 다음과 같이 정의될 수 있습니다:
from eiffel import eiffelmethod
class C(object):
def f(self, arg1, arg2):
# 실제 함수
...
def pre_f(self):
# 전제 조건 확인
...
def post_f(self):
# 사후 조건 확인
...
f = eiffelmethod(f, pre_f, post_f)
새로운 eiffelmethod`를 사용하는 사람이 디스크립터에 대한 어떤 지식도 필요하지 않다는 점에 주목하십시오. 이것이 제가 이 새로운 기능들이 언어의 기본적인 복잡도를 높이지 않는다고 생각하는 이유입니다. :func:()!eiffelmethod`나 ZODB 또는 무엇을 작성하려면 알아야 하는 소수의 전문가가 있겠지만, 대부분의 사용자는 결과로 나오는 라이브러리 위에 코드를 작성하고 구현 세부 사항은 무시하게 될 것입니다.
다중 상속: 다이아몬드 규칙¶
이름을 해결하는 규칙을 변경함으로써 다중 상속도 더 유용하게 되었습니다. 다음 클래스 세트를 고려해 보십시오 (다이어그램은 Guido van Rossum의 :pep:`253`에서 가져옴):
class A:
^ ^ def save(self): ...
/ \
/ \
/ \
/ \
class B class C:
^ ^ def save(self): ...
\ /
\ /
\ /
\ /
class D
클래식 클래스의 조회 규칙은 간단하지만 지능적이지 않습니다. 기본 클래스들은 왼쪽에서 오른쪽으로 깊이 우선 탐색되며 검색됩니다. :meth:!D.save`에 대한 참조는 D, B, 그리고 이후 A`를 순서대로 검색할 것이며, 여기서 :meth:!save`가 찾아져 반환될 것입니다. :meth:!C.save`는 절대 발견되지 않습니다. 이것은 문제가 되는데, 만약 C`의 :meth:!save` 메서드가 :class:`!C`에 특화된 내부 상태를 저장하고 있다면, 이 메서드를 호출하지 않으면 그 상태가 영원히 저장되지 않게 됩니다.
뉴스타일 클래스는 설명하기 다소 복잡하지만 올바르게 작동하는 다른 알고리즘을 따릅니다. (참고: Python 2.3에서는 이 알고리즘을 대부분의 경우 동일한 결과를 내지만, 매우 복잡한 상속 그래프에 대해서는 더 유용한 결과를 내도록 변경합니다.)
전통적인 조회 규칙을 따르는 모든 기본 클래스를 나열하고, 반복적으로 방문되는 경우 해당 클래스는 여러 번 포함하십시오. 위의 예시에서 방문된 클래스 목록은 [
D,B,A,C,A]입니다.목록을 중복된 클래스를 검색합니다. 만약 발견되면, 한 번만 남기고 모두 제거하며 목록에는 마지막 항목만 남깁니다. 위의 예시에서는 중복을 제거한 후 목록이 [
D,B,C,A]가 됩니다.
이 규칙을 따르면, :meth:!D.save`에 대한 참조는 우리가 원하는 동작인 C.save`를 반환합니다. 이 조회 규칙은 Common Lisp가 따르는 것과 같습니다. 새로운 내장 함수인 :func:`super`는 Python의 알고리즘을 재구현하지 않고도 클래스의 슈퍼클래스에 접근하는 방법을 제공합니다. 가장 일반적으로 사용되는 형태는 ``super(class, obj)``이며, 이는 바운드 슈퍼클래스 객체(실제 클래스 객체는 아님)를 반환합니다. 이 형태는 슈퍼클래스의 메서드를 호출할 때 메서드 내에서 사용됩니다. 예를 들어, :class:()!D`의 save() 메서드는 다음과 같을 것입니다:
class D (B,C):
def save (self):
# 슈퍼클래스의 .save() 호출
super(D, self).save()
# 여기에 D의 private 정보를 저장합니다.
...
super() 는 super(class) 또는 super(class1, class2) 와 같이 호출될 때 바운드되지 않은 슈퍼클래스 객체도 반환할 수 있지만, 이것은 아마 자주 유용하지 않을 것입니다.
어트리뷰트 액세스¶
다수의 정교한 Python 클래스는 __getattr__() 를 사용하여 어트리뷰트 액세스를 위한 훅을 정의합니다. 대부분의 경우 이것은 편의를 위해 수행되며, 예를 들어 obj.parent 와 같은 어트리뷰트 액세스가 obj.get_parent 와 같은 메서드 호출로 자동 매핑되도록 코드를 더 읽기 쉽게 만드는 데 사용됩니다. Python 2.2는 어트리뷰트 액세스를 제어하는 몇 가지 새로운 방법을 추가합니다.
첫째, __getattr__(attr_name) 은 여전히 뉴스타일 클래스에서 지원되며, 이에 변화가 없습니다. 이전과 마찬가지로, 인스턴스의 딕셔너리에서 foo 라는 이름의 어트리뷰트를 찾으려고 시도할 때 호출됩니다.
뉴스타일 클래스는 또한 새로운 메서드인 __getattribute__(attr_name) 을 지원합니다. 두 메서드의 차이점은 __getattribute__() 는 어트리뷰트에 접근할 때 항상 호출되는 반면, 이전의 __getattr__() 는 인스턴스의 딕셔너리에 foo 가 없을 때만 호출된다는 점입니다.
하지만 Python 2.2의 properties 지원은 종종 어트리뷰트 참조를 가로채는 더 간단한 방법이 될 것입니다. __getattr__() 메서드를 작성하는 것은 복잡합니다. 재귀를 피하기 위해서는 그 안에서 일반적인 어트리뷰트 액세스를 사용할 수 없으며, 대신 __getattr__ 메서드는 또한 Python이 __repr__() 또는 :meth:`!__coerce__`와 같은 다른 메서드를 확인할 때 호출되므로, 이를 염두에 두고 작성해야 합니다. 마지막으로, 모든 어트리뷰트 액세스 시 함수를 호출하면 상당한 성능 저하가 발생합니다.
property`는 값을 가져오거나, 설정하거나, 삭제하는 세 개의 함수와 docstring을 패키징하는 새로운 내장 타입입니다. 예를 들어, 계산되지만 설정할 수도 있는 :attr:!size` 어트리뷰트를 정의하려면 다음과 같이 작성할 수 있습니다:
class C(object):
def get_size (self):
result = ... 계산 결과 ...
return result
def set_size (self, size):
... 크기에 따라 무언가를 계산하고 내부 상태를 적절하게 설정합니다 ...
# 속성을 정의합니다. '이 속성을 삭제하세요'
# 메서드는 None으로 정의되어 있으므로 이 속성은 삭제할 수 없습니다.
size = property(get_size, set_size,
None,
"인스턴스의 저장 크기")
이 방식은 __getattr__()/__setattr__() 메서드 쌍을 사용하는 것보다 훨씬 명확하고 작성하기 쉽습니다. 이 메서드 쌍은 :attr:!size` 속성을 확인하고 특별히 처리하며, 다른 모든 속성은 인스턴스의 또한, :attr:!size`에 대한 접근 역시 함수를 호출하는 작업을 수행해야 하는 유일한 경우이므로, 다른 속성에 대한 참조는 평소와 같은 속도로 실행됩니다.
마지막으로, 새로운 __slots__ 클래스 속성을 사용하여 객체에서 참조할 수 있는 속성 목록을 제한하는 것이 가능해졌습니다. 파이썬 객체는 보통 매우 동적입니다. 언제든지 obj.new_attr=1 을 실행하여 인스턴스에 새 속성을 정의할 수 있습니다. 새로운 스타일의 클래스는 __slots__ 이라는 이름의 클래스 속성을 정의하여 허용되는 속성을 특정 이름 세트로 제한할 수 있습니다. 예제가 이를 명확히 보여줄 것입니다:
>>> class C(object):
... __slots__ = ('template', 'name')
...
>>> obj = C()
>>> print obj.template
None
>>> obj.template = 'Test'
>>> print obj.template
Test
>>> obj.newattr = None
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'C' object has no attribute 'newattr'
:attr:`~object.__slots__`에 나열되지 않은 속성에 할당을 시도할 때 :exc:`AttributeError`가 발생하는 것을 확인하세요.
PEP 234: 이터레이터¶
2.2 버전에서 또 다른 중요한 추가 사항은 C 및 Python 레벨 모두에서 반복 인터페이스입니다. 객체는 호출자에게 자신이 어떻게 순회될 수 있는지 정의할 수 있습니다.
Python 2.1 버전까지에서는 for item in obj 를 작동시키는 일반적인 방법은 다음과 같은 __getitem__() 메서드를 정의하는 것입니다:
def __getitem__(self, index):
return <next item>
__getitem__() 는 객체에 인덱싱 연산을 정의하는 데 더 적절하게 사용되어, obj[5] 를 사용하여 여섯 번째 요소를 검색할 수 있습니다. 이것을 오직 for 루프를 지원하기 위해서만 사용하는 것은 다소 오해의 소지가 있습니다. 순회되기를 원하는 파일 같은 객체를 고려해보세요. index 매개변수는 본질적으로 의미가 없습니다. 왜냐하면 해당 클래스는 아마도 일련의 __getitem__() 호출이 매번 index 를 1씩 증가시키며 이루어질 것이라고 가정하기 때문입니다. 다시 말해, __getitem__() 메서드의 존재 자체가 file[5] 를 사용하여 여섯 번째 요소를 무작위로 접근할 수 있다는 것을 의미하지는 않지만, 실제로는 그렇게 작동해야 합니다.
파이썬 2.2에서는 이터레이션을 별도로 구현할 수 있게 되었으며, __getitem__() 메서드는 인접 접근을 실제로 지원하는 클래스로 제한될 수 있습니다. 이터레이터의 기본적인 개념은 간단합니다. 새로운 내장 함수인 iter(obj) 또는 iter(C, sentinel) 를 사용하여 이터레이터를 얻습니다. iter(obj) 는 객체 obj 에 대한 이터레이터를 반환하는 반면, iter(C, sentinel) 는 콜러블 객체 C 가 sentinel 을 반환할 때까지 호출하여 이터레이터를 반환합니다.
파이썬 클래스는 __iter__() 메서드를 정의할 수 있으며, 이 메서드는 객체에 대한 새 이터레이터를 생성하고 반환해야 합니다. 만약 객체가 자신만의 이터레이터라면, 이 메서드는 단순히 self 를 반환해도 됩니다. 특히, 이터레이터는 일반적으로 자신을 이터레이터로 사용합니다. C로 구현된 확장 타입은 tp_iter 함수를 구현하여 이터레이터를 반환할 수 있으며, 이터레이터처럼 동작하려는 확장 타입은 tp_iternext 함수를 정의할 수도 있습니다.
사실 이 모든 것을 거쳐서 이터레이터는 실제로 무엇을 할까요? 이들은 인수가 없고 다음 값을 반환하는 필수 메서드인 next`를 가지고 있습니다. 반환할 더 이상 값이 없을 때, :meth:`next() 호출은 StopIteration 예외를 발생시켜야 합니다.
>>> L = [1,2,3]
>>> i = iter(L)
>>> print i
<iterator object at 0x8116870>
>>> i.next()
1
>>> i.next()
2
>>> i.next()
3
>>> i.next()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
StopIteration
>>>
2.2 버전부터 파이썬의 for 문은 더 이상 시퀀스를 기대하지 않습니다. 대신, iter() 가 이터레이터를 반환할 수 있는 것을 기대합니다. 하위 호환성과 편의를 위해, __iter__() 나 tp_iter 슬롯을 구현하지 않은 시퀀스에 대해서는 자동으로 이터레이터가 생성되므로 for i in [1,2,3] 는 여전히 작동합니다. 파이썬 인터프리터가 시퀀스를 반복할 때는 사용되는 것이 이터레이터 프로토콜로 변경되었습니다. 이는 다음과 같은 작업을 할 수 있다는 의미입니다:
>>> L = [1,2,3]
>>> i = iter(L)
>>> a,b,c = i
>>> a,b,c
(1, 2, 3)
일부 파이썬 기본 타입에는 이터레이터 지원 기능이 추가되었습니다. 딕셔너리에 :func:`iter`를 호출하면 해당 딕셔너리의 키를 반복하는 이터레이터가 반환됩니다:
>>> m = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6,
... 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12}
>>> for key in m: print key, m[key]
...
Mar 3
Feb 2
Aug 8
Sep 9
May 5
Jun 6
Jul 7
Jan 1
Apr 4
Nov 11
Dec 12
Oct 10
그것은 단지 기본 동작입니다. 만약 키나 값, 또는 키/값 쌍을 순회하려면, 적절한 이터레이터를 얻기 위해 iterkeys(), itervalues(), 또는 iteritems() 메서드를 명시적으로 호출할 수 있습니다. 사소한 관련 변경 사항으로, 이제 in 연산자는 딕셔너리에서도 작동하므로 key in dict 는 이제 dict.has_key(key) 와 동일합니다.
파일도 이터레이터를 제공하여, 파일에 더 이상 줄이 없을 때까지 readline() 메서드를 호출합니다. 즉, 다음과 같은 코드를 사용하여 파일의 각 줄을 읽을 수 있습니다:
for line in file:
# do something for each line
...
이터레이터에서는 오직 앞으로만 나아갈 수 있다는 점에 유의하세요. 이전 요소를 가져오거나, 이터레이터를 재설정하거나, 복사본을 만들 방법은 없습니다. 이터레이터 객체는 이러한 추가 기능을 제공할 수는 있지만, 이터레이터 프로토콜은 next() 메서드만을 요구합니다.
더 보기
- PEP 234 - 이터레이터
Ka-Ping Yee와 GvR가 작성했으며, Python Labs 팀이 주로 GvR와 Tim Peters를 통해 구현했습니다.
PEP 255: 간단한 제너레이터¶
제너레이터는 이터레이터의 도입과 상호 작용하는 또 다른 새로운 기능입니다.
파이썬이나 C에서 함수 호출이 어떻게 작동하는지 잘 알고 계실 것입니다. 함수를 호출하면 지역 변수가 생성되는 비공개 이름 공간을 얻습니다. 함수가 return 문에 도달하면, 지역 변수는 파괴되고 그 결과 값이 호출자에게 반환됩니다. 같은 함수를 나중에 호출하면 새로운 지역 변수 집합이 새로 생성됩니다. 하지만, 지역 변수가 함수 종료 시 버려지지 않는다면 어떨까요? 나중에 중단했던 곳에서 함수를 다시 시작할 수 있다면 어떨까요? 이것이 제너레이터가 제공하는 것입니다. 그들은 재개 가능한 함수라고 생각할 수 있습니다.
다음은 제너레이터 함수의 가장 간단한 예입니다:
def generate_ints(N):
for i in range(N):
yield i
yield``를 위해 새로운 키워드가 도입되었습니다. :keyword:!yield`` 문을 포함하는 함수는 제너레이터 함수이며, 이는 파이썬의 바이트코드 컴파일러가 전문적으로 컴파일하여 감지합니다. 새로운 키워드가 도입되었기 때문에, 제너레이터를 사용하려면 모듈 상단에 from __future__ import generators 구문을 포함하여 명시적으로 활성화해야 합니다. Python 2.3에서는 이 구문이 필요하지 않게 될 것입니다.
제너레이터 함수를 호출하면 단일 값을 반환하는 것이 아니라, 대신 이터레이터 프로토콜을 지원하는 제너레이터 객체를 반환합니다. yield 문을 실행함으로써, 제너레이터는 i 의 값을 출력하며 이는 return 문과 유사합니다. yield` 와 return 문 사이의 큰 차이점은, yield` 에 도달하면 제너레이터의 실행 상태가 일시 중단되고 지역 변수가 보존된다는 것입니다. 다음 번 제너레이터의 next() 메서드가 호출되면, 함수는 yield` 문 바로 다음에 실행을 재개합니다. (복잡한 이유로 인해, yield 문은 구조화된 try…finally 문의 try 블록 내부에서는 허용되지 않습니다. yield 와 예외 간의 상호 작용에 대한 전체 설명은 :pep:`255`를 참조하십시오.)
Here’s a sample usage of the generate_ints() generator:
>>> gen = generate_ints(3)
>>> gen
<generator object at 0x8117f90>
>>> gen.next()
0
>>> gen.next()
1
>>> gen.next()
2
>>> gen.next()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 2, in generate_ints
StopIteration
여러분은 똑같이 for i in generate_ints(5) 또는 a,b,c = generate_ints(3) 라고 작성할 수 있습니다.
제너레이터 함수 내부에서, return 문은 값 없이 사용될 수 있으며, 값의 순서 끝을 알립니다; 그 이후로는 제너레이터가 더 이상 값을 반환할 수 없습니다. return 5 와 같이 값을 가지는 return 는 제너레이터 함수 내부에서 구문 오류입니다. 제너레이터 결과의 끝은 또한 StopIteration 을 수동으로 발생시키거나, 실행 흐름이 함수의 바닥으로 떨어지게 하여 나타낼 수도 있습니다.
직접 클래스를 작성하고 제너레이터의 모든 지역 변수를 인스턴스 변수로 저장하여 제너레이터의 효과를 수동으로 구현할 수 있습니다. 예를 들어, 정수 리스트를 반환하는 것은 self.count 를 0으로 설정하고 next() 메서드에서 self.count 를 증가시켜 반환함으로써 수행될 수 있습니다. 하지만, 다소 복잡한 제너레이터의 경우 해당 클래스를 작성하는 것이 훨씬 더 지저분할 것입니다. Lib/test_/test_generators.py 파일에는 더 흥미로운 여러 예제가 포함되어 있습니다. 이 중 가장 간단한 것은 제너레이터를 사용하여 트리를 중위 순회하는 것을 재귀적으로 구현합니다.
# 트리에서 리프 노드를 중위(in-order)로 생성하는 재귀적 제너레이터입니다.
def inorder(t):
if t:
for x in inorder(t.left):
yield x
yield t.label
for x in inorder(t.right):
yield x
`:file:`Lib/test/test_generators.py`의 다른 두 가지 예제는 N-여왕 문제($N$개의 여왕을 $N times N$ 체스판에 배치하여 어떤 여왕도 서로 위협하지 않게 하는 문제)와 기사의 여행(기사가 $N times N$ 체스판의 모든 칸을 두 번 방문하지 않고 지나가는 경로를 찾는 문제)의 해답을 제공합니다.
제너레이터의 아이디어는 다른 프로그래밍 언어들, 특히 Icon(https://www2.cs.arizona.edu/icon/)에서 왔으며, 이곳에서는 제너레이터의 개념이 핵심입니다. Icon에서는 모든 표현식과 함수 호출이 제너레이터처럼 동작합니다. https://www2.cs.arizona.edu/icon/docs/ipd266.htm에 있는 “Icon 프로그래밍 언어 개요” 예제는 이게 어떻게 생겼는지 감을 잡게 해줍니다:
sentence := "Store it in the neighboring harbor"
if (i := find("or", sentence)) > 5 then write(i)
In Icon the find() function returns the indexes at which the substring
“or” is found: 3, 23, 33. In the if statement, i is first
assigned a value of 3, but 3 is less than 5, so the comparison fails, and Icon
retries it with the second value of 23. 23 is greater than 5, so the comparison
now succeeds, and the code prints the value 23 to the screen.
Python doesn’t go nearly as far as Icon in adopting generators as a central concept. Generators are considered a new part of the core Python language, but learning or using them isn’t compulsory; if they don’t solve any problems that you have, feel free to ignore them. One novel feature of Python’s interface as compared to Icon’s is that a generator’s state is represented as a concrete object (the iterator) that can be passed around to other functions or stored in a data structure.
더 보기
- PEP 255 - 단순 제너레이터
작성자: Neil Schemenauer, Tim Peters, Magnus Lie Hetland. 구현은 주로 Neil Schemenauer와 Tim Peters가 담당했으며, Python Labs 크루의 다른 수정 사항들이 추가되었습니다.
PEP 237: Long Integers와 Integer의 통일화 (Unifying Long Integers and Integers)¶
최신 버전에서는 대부분의 기계에서 32비트 값인 일반 정수와 임의 크기일 수 있는 긴 정수 사이의 구분이 불편함을 야기했습니다. 예를 들어, 2**32 바이트보다 큰 파일을 지원하는 플랫폼에서는 파일 객체의 tell() 메서드가 긴 정수를 반환해야 했습니다. 하지만 파이썬의 여러 부분은 일반 정수를 예상했기 때문에, 대신 긴 정수가 제공되면 오류가 발생했습니다. 예를 들어, Python 1.5에서는 슬라이스 인덱스로는 일반 정수만 사용할 수 있었고, 'abc'[1L:] 는 ‘슬라이스 인덱스는 int여야 합니다’라는 메시지와 함께 TypeError 예외를 발생시켰습니다.
Python 2.2부터는 필요한 경우 짧은 정수에서 긴 정수로 값을 변환할 것입니다. 컴파일러가 적절한 타입을 선택함에 따라 더 이상 ‘L’ 접미사로 긴 정수 리터럴을 나타낼 필요가 없습니다 (향후 2.x 버전의 Python에서는 ‘L’ 사용이 권장되지 않으며, Python 2.4에서 경고가 발생하고 아마도 Python 3.0에서 제거됩니다). 이전에는 :exc:`OverflowError`를 발생시키던 많은 연산들이 이제 그 결과로 긴 정수를 반환합니다. 예를 들어:
>>> 1234567890123
1234567890123L
>>> 2 ** 64
18446744073709551616L
대부분의 경우, 일반 정수와 긴 정수는 동일하게 취급될 것입니다. 여전히 type() 내장 함수를 사용하여 구별할 수 있지만, 이는 거의 필요하지 않습니다.
더 보기
- PEP 237 - Long Integers와 Integer의 통일화 (Unifying Long Integers and Integers)
작성자: Moshe Zadka 및 Guido van Rossum. 주로 Guido van Rossum가 구현함.
PEP 238: 나눗셈 연산자 변경¶
Python 2.2에서 가장 논란이 많은 변화는 파이썬 초창기부터 존재했던 오래된 설계 결함을 수정하려는 노력의 시작을 알립니다. 현재 파이썬의 나눗셈 연산자 /``는 두 개의 정수 인수를 받을 때 C 언어의 나눗셈 연산자처럼 동작합니다. 즉, 소수 부분이 있을 경우 버려진(truncated down) 정수 결과를 반환합니다. 예를 들어, ``3/2 는 1이며 1.5가 아니고, (-1)/2 는 -1이며 -0.5가 아닙니다. 이는 나눗셈의 결과가 두 피연산자의 타입에 따라 예상치 못하게 달라질 수 있음을 의미하며, 파이썬은 동적 타이핑 언어이므로 피연산자의 가능한 타입을 결정하기 어려울 수 있습니다.
(논란의 여지는 이것이 정말 설계 결함인지, 아니면 이를 수정하기 위해 기존 코드를 깨뜨릴 가치가 있는지에 관한 것입니다. 이 문제는 python-dev에서 끝없는 토론을 불러일으켰고, 2001년 7월에는 comp.lang.python 에서 산성 풍자 게시물의 폭풍으로 터졌습니다. 저는 어느 쪽도 주장하지 않고 2.2에 구현된 내용을 설명하는 데 중점을 두겠습니다. 논거와 반론의 요약은 :pep:`238`을 참조하십시오.)
이 변화는 코드를 깨뜨릴 수 있으므로 매우 점진적으로 도입되고 있습니다. Python 2.2가 전환을 시작하지만, 이 전환은 Python 3.0까지 완료되지 않습니다.
먼저, PEP 238 에서 용어 몇 가지를 빌리겠습니다. ‘True division’는 대부분의 비프로그래머가 익숙한 나눗셈입니다. 즉, 3/2 는 1.5, 1/4 는 0.25 등입니다. ‘Floor division’은 파이썬의 / 연산자가 정수 피연산자를 받을 때 현재 수행하는 작업이며, 그 결과는 True division으로 반환된 값의 내림(floor)값입니다. ‘Classic division’은 / 의 현재 혼합 동작입니다. 피연산자가 정수일 때는 Floor division 결과를 반환하고, 피연산자 중 하나가 부동소수점 숫자인 경우에는 True division의 결과를 반환합니다.
2.2에서 도입되는 변경 사항은 다음과 같습니다:
새 연산자인
//``는 바닥 나눗셈 연산자입니다. (네, C++의 주석 기호와 비슷해 보인다는 것을 알고 있습니다.) ``//``*는 피연산자의 자료형에 관계없이 항상 바닥 나눗셈을 수행하므로, ``1 // 2``는 0이며 ``1.0 // 2.0또한 0.0입니다.//``는 파이썬 2.2에서 항상 사용 가능하며, ``__future__문을 사용하여 활성화할 필요가 없습니다.모듈에
from __future__ import division를 포함하면/연산자는 진수 나누기(true division)의 결과를 반환하도록 변경되어,1/2는 0.5가 됩니다.__future__문이 없으면,/는 여전히 클래식 나눗셈을 의미합니다. 기본 의미의/는 파이썬 3.0까지 변경되지 않습니다.클래스는 두 가지 나누기 연산자를 오버로드하기 위해
C 레벨에서는 또한 :c:type:`PyNumberMethods()구조체에 슬롯이 있어 확장 타입들이 두 연산자를 정의할 수 있도록 합니다.Python 2.2는 변경된 나눗셈 의미와 함께 코드가 작동하는지 테스트하기 위한 일부 명령줄 인수를 지원합니다. python`을 :option:!-Q warn`으로 실행하면 정수 두 개에 나누기가 적용될 때 경고가 발생합니다. 이를 사용하여 변경 사항의 영향을 받는 코드를 찾고 수정할 수 있습니다. 기본적으로 Python 2.2는 경고 없이 단순히 클래식 나눗셈을 수행하며, 이 경고는 Python 2.3에서 기본으로 활성화됩니다.
더 보기
- PEP 238 - 나누기 연산자 변경
작성자: Moshe Zadka와 Guido van Rossum. 구현: Guido van Rossum..
유니코드 변경사항¶
파이썬의 유니코드 지원은 2.2 버전에서 약간 향상되었습니다. 유니코드 문자열은 일반적으로 16비트 부호 없는 정수형인 UCS-2로 저장됩니다. Python 2.2는 또한 configure 스크립트에 --enable-unicode=ucs4`를 지정하여 내부 인코딩으로 32비트 부호 없는 정수인 UCS-4를 사용하도록 컴파일될 수도 있습니다. (유니코드 지원을 완전히 비활성화하려면 :option:!–disable-unicode`도 지정할 수 있습니다.)
UCS-4(광폭 Python, “wide Python”)를 사용하도록 빌드된 경우, 인터프리터는 U+000000부터 U+110000까지의 유니코드 문자를 기본적으로 처리할 수 있으므로 unichr() 함수의 법적 값 범위도 그에 따라 확장됩니다. UCS-2(협폭 Python, “narrow Python”)를 사용하도록 컴파일된 인터프리터를 사용하는 경우, 65535보다 큰 값은 여전히 unichr`가 :exc:`ValueError() 예외를 발생시키게 합니다. 이 모든 것이 PEP 261, “‘광폭’ 유니코드 문자 지원”에 설명되어 있습니다. 자세한 내용은 해당 문서를 참조하십시오.
좀 더 간단하게 설명할 수 있는 또 다른 변경 사항이 있습니다. 소개된 이후로, 유니코드 문자열은 UTF-8 또는 Latin-1과 같은 선택한 인코딩으로 문자열을 변환하는 encode() 메서드를 지원해 왔습니다. 2.2 버전에서는 (유니코드 문자열에는 아니지만) 8비트 문자열에 대칭적인 decode([*encoding*]) 메서드가 추가되었습니다. :meth:`!decode`는 문자열이 지정된 인코딩에 있다고 가정하고 이를 디코딩하여, 코덱이 반환하는 모든 것을 반환합니다.
이 새로운 기능을 사용함으로써, 코덱은 유니코드와 직접 관련 없는 작업을 위해 추가되었습니다. 예를 들어, 코덱은 uu-인코딩, MIME의 base64 인코딩, 그리고 zlib 모듈을 사용한 압축에 대해 추가되었습니다:
>>> s = """Here is a lengthy piece of redundant, overly verbose,
... and repetitive text.
... """
>>> data = s.encode('zlib')
>>> data
'x\x9c\r\xc9\xc1\r\x80 \x10\x04\xc0?Ul...'
>>> data.decode('zlib')
'Here is a lengthy piece of redundant, overly verbose,\nand repetitive text.\n'
>>> print s.encode('uu')
begin 666 <data>
M2&5R92!I<R!A(&QE;F=T:'D@<&EE8V4@;V8@<F5D=6YD86YT+"!O=F5R;'D@
>=F5R8F]S92P*86YD(')E<&5T:71I=F4@=&5X="X*
end
>>> "sheesh".encode('rot-13')
'furrfu'
클래스가 __unicode__() 메서드를 정의하여 클래스 인스턴스를 유니코드로 변환할 수 있으며, 이는 :meth:`!__str__`과 유사합니다.
encode(), decode(), 및 :meth:`!__unicode__`는 Marc-André Lemburg가 구현했습니다. 내부적으로 UCS-4를 사용하도록 지원하는 변경 사항은 Fredrik Lundh와 Martin von Löwis가 구현했습니다.
더 보기
- PEP 261 - ‘광폭’ 유니코드 문자 지원
작성자: Paul Prescod.
PEP 227: 중첩 스코프¶
Python 2.1에서 정적으로 중첩된 스코프는 선택적 기능으로 추가되었으며, from __future__ import nested_scopes 지시어를 통해 활성화해야 했습니다. 2.2 버전부터는 중첩 스코프를 특별히 활성화할 필요가 없어졌고 항상 존재합니다. 이 섹션의 나머지는 제 “Python 2.1에서 새로워진 기능” 문서에 있는 중첩 스코프 설명 복사본입니다. 만약 2.1이 출시되었을 때 이 문서를 읽었다면, 이 섹션은 건너뛰셔도 됩니다.
파이썬의 스코핑 규칙에서 도입된 가장 큰 변경 사항은 Python 2.1에 등장했으며, 2.2에서 완성되었습니다. Python 2.0에서는 주어진 시점에 변수 이름을 조회하는 데 최대 세 개의 이름 공간—지역(local), 모듈 레벨(module-level), 및 내장 네임스페이스(built-in namespace)—이 사용됩니다. 이는 직관적인 예상과 일치하지 않아 사람들을 많이 놀라게 했습니다. 예를 들어, 중첩된 재귀 함수 정의는 작동하지 않습니다:
def f():
...
def g(value):
...
return g(value-1) + 1
...
함수 g() 은 항상 NameError 예외를 발생시킵니다. 이는 이름 g 의 바인딩이 지역 네임스페이스나 모듈 레벨 네임스페이스 어느 것에도 없기 때문입니다. 실제로는 큰 문제가 아닙니다(이처럼 내부 함수를 재귀적으로 얼마나 자주 정의하나요?). 그러나 이로 인해 lambda 표현식 사용도 불편해졌고, 이는 실제 문제였습니다. 다음과 같이 lambda 를 사용하는 코드에서는 종종 지역 변수들이 인자의 기본값을 전달함으로써 복사되는 것을 발견할 수 있습니다.
def find(self, name):
"Return list of any entries equal to 'name'"
L = filter(lambda x, name=name: x == name,
self.list_attribute)
return L
이러한 방식으로 작성된 Python 코드의 가독성은 크게 손상됩니다.
Python 2.2에서 가장 중요한 변경 사항은 이 문제를 해결하기 위해 언어에 정적 스코핑(static scoping)이 추가되었다는 것입니다. 첫 번째 영향으로, 위 예제에서는 더 이상 name=name 기본 인자가 필요하지 않게 되었습니다. 간단히 말해, 주어진 변수 이름이 함수 내에서 (대입을 통한 방식이나 def, class 또는 import 문에 의한 방식) 값이 할당되지 않은 경우, 해당 변수의 참조는 포함하는(enclosing) 스코프의 지역 네임스페이스에서 조회됩니다. 규칙에 대한 더 자세한 설명과 구현 분석은 PEP에서 찾을 수 있습니다.
이러한 변경 사항은 모듈 레벨과 함수 내부의 로컬 변수로 동일한 변수 이름이 사용되는 코드에 일부 호환성 문제를 일으킬 수 있습니다. 하지만 기본적으로 이런 코드는 애초에 읽기가 꽤 혼란스러웠을 테니, 그럴 가능성은 낮아 보입니다.
이러한 변경 사항의 하나의 부작용은 특정 조건 하에서 from module import * 및 exec 문장이 함수 스코프 안에서 불법적이 되었다는 것입니다. Python 참조 매뉴얼은 항상 from module import * 가 모듈의 최상위 레벨에서만 유효하다고 말했지만, CPython 인터프리터는 이전에 이를 강제한 적이 없습니다. 중첩된 스코프를 구현하는 과정에서, Python 소스 코드를 바이트코드로 변환하는 컴파일러는 포함하는 스코프의 변수에 접근하기 위해 다른 코드를 생성해야 합니다. from module import * 및 exec 는 컴파일 시간에 알 수 없는 지역 네임스페이스에 이름을 추가하기 때문에 컴파일러가 이를 판단하는 것을 불가능하게 만듭니다. 따라서 함수가 자유 변수(free variables)를 가진 함수 정의나 lambda 표현식을 포함하면, 컴파일러는 SyntaxError 예외를 발생시켜 이를 플래그합니다.
앞선 설명을 조금 더 명확히 하기 위해, 여기 예제가 있습니다:
x = 1
def f():
# 다음 줄은 구문 오류입니다.
exec 'x=2'
def g():
return x
exec 문이 포함된 라인 4는 x 의 값이 g() 에 의해 접근되어야 할 새로운 로컬 변수를 정의하므로 구문 오류입니다.
이는 큰 제한 사항은 아닐 것입니다. 왜냐하면 exec 는 대부분의 Python 코드에서 거의 사용되지 않으며 (사용되는 경우에도, 이는 종종 나쁜 디자인의 신호일 뿐이기 때문입니다).
더 보기
- PEP 227 - 정적 중첩 스코프(Statically Nested Scopes)
작성 및 구현자: Jeremy Hylton.
개선 및 신규 모듈¶
xmlrpclib모듈은 Fredrik Lundh가 표준 라이브러리에 기여했으며, XML-RPC 클라이언트를 작성하는 지원을 제공합니다. XML-RPC는 HTTP 및 XML을 기반으로 구축된 단순한 원격 프로시저 호출 프로토콜입니다. 예를 들어, 다음 코드 스니펫은 O’Reilly Network에서 RSS 채널 목록을 검색하고, 그런 다음 하나의 채널에 대한 최근 헤드라인 목록을 나열합니다:import xmlrpclib s = xmlrpclib.Server( 'http://www.oreillynet.com/meerkat/xml-rpc/server.php') channels = s.meerkat.getChannels() # channels는 딕셔너리 리스트이며, 다음과 같은 형태입니다: # [{'id': 4, 'title': 'Freshmeat Daily News'} # {'id': 190, 'title': '32Bits Online'}, # {'id': 4549, 'title': '3DGamers'}, ... ] # 특정 채널의 항목을 가져옵니다. items = s.meerkat.getItems( {'channel': 4} ) # 'items'도 다음과 같은 형태의 또 다른 딕셔너리 리스트입니다: # [{'link': 'http://freshmeat.net/releases/52719/', # 'description': 'HTML을 XSL FO로 변환하는 유틸리티.', # 'title': 'html2fo 0.3 (기본값)'}, ... ]
SimpleXMLRPCServer모듈을 사용하면 간단한 XML-RPC 서버를 쉽게 만들 수 있습니다. XML-RPC에 대한 자세한 정보는 http://xmlrpc.scripting.com/ 을 참조하세요.새로운
hmac모듈은 :rfc:`2104`에서 설명하는 HMAC 알고리즘을 구현합니다. (Gerhard Häring 기여.)원래 길게 튜플을 반환하던 여러 함수들이 이제는 여전히 튜플처럼 작동하지만,
memberst_mtime또는기능이 향상된 함수로는 :mod:`os모듈의stat(),fstat(),statvfs(), 및fstatvfs`가 있으며, :mod:`time()모듈에는localtime(),gmtime(), 및 :func:`~time.strptime`이 포함됩니다.예를 들어, 이전 튜플을 사용하여 파일 크기를 가져올 때는
file_size = os.stat(filename)[stat.ST_SIZE]와 같은 코드를 작성해야 했지만, 이제는 이 코드를 더 명확하게file_size = os.stat(filename).st_size로 작성할 수 있습니다.이 기능의 원래 패치는 Nick Mathewson이 기여했습니다.
파이썬 프로파일러가 대대적으로 재작업되었고 출력 내 다양한 오류들이 수정되었습니다. (Fred L. Drake, Jr.와 Tim Peters 기여.)
socket모듈은 IPv6을 지원하도록 컴파일될 수 있습니다. Python의 configure 스크립트에서--enable-ipv6옵션을 지정하세요. (Jun-ichiro “itojun” Hagino 기여.)struct모듈에 C long long 타입을 지원하는 플랫폼용 64비트 정수에 대한 두 가지 새로운 형식 문자가 추가되었습니다.q는 부호 있는 64비트 정수용이며,Q는 부호 없는 정수용입니다. 값은 Python의 long 정수 타입으로 반환됩니다. (Tim Peters 기여.)인터프리터의 인터랙티브 모드에서는
pydoc모듈(Python 2.1에 도입됨)을 사용하는 새로운 내장 함수help`를 통해 대화형 도움말을 제공합니다. ``help(object)``는 *object*에 대한 사용 가능한 모든 도움말 텍스트를 표시합니다. 인수가 없는 :func:`help`는 온라인 도움말 유틸리티로 진입하며, 여기서 함수, 클래스 또는 모듈 이름을 입력하여 해당 도움말 텍스트를 읽을 수 있습니다. (Guido van Rossum 기여, Ka-Ping Yee의 :mod:`pydoc()모듈 사용.)re모듈의 기반이 되는 SRE 엔진에 다양한 버그 수정 및 성능 개선이 이루어졌습니다. 예를 들어,re.sub`와 :func:`re.split()함수는 C로 재작성되었습니다. 또 다른 기여 패치는 특정 유니코드 문자 범위의 속도를 두 배로 높였고, 주어진 문자열에서 모든 중복되지 않는 일치 항목에 대한 반복자(iterator)를 반환하는 새로운finditer()메서드가 추가되었습니다. (SRE는 Fredrik Lundh가 유지 관리합니다. BIGCHARSET 패치는 Martin von Löwis가 기여했습니다.)smtplib모듈은 이제 RFC 2487, “Secure SMTP over TLS”를 지원하므로, Python 프로그램과 메시지를 전달받는 메일 전송 에이전트 간의 SMTP 트래픽을 암호화하는 것이 가능합니다. 또한, :mod:`smtplib`은 SMTP 인증도 지원합니다. (Gerhard Häring 기여.)Piers Lauder가 유지 관리하는
imaplib모듈은 여러 새로운 확장에 대한 지원을 추가했습니다: :rfc:`2342`에 정의된 NAMESPACE, SORT, GETACL 및 SETACL입니다. (Anthony Baxter와 Michel Pelletier 기여.)rfc822모듈에서 이메일 주소를 파싱하는 것은 이제 :rfc:`2822`를 준수합니다. 이는 :rfc:`822`의 업데이트 내용입니다. (모듈 이름은 거의 rfc2822로 변경되지 않습니다.) 또한, e-mail 메시지를 파싱하고 생성하기 위한 새로운 패키지인 :mod:`email`도 추가되었습니다. (역사적 기여: Barry Warsaw, Mailman 작업 기반)difflib모듈에는 이제 두 개의 텍스트 라인 시퀀스 간의 인간이 읽기 쉬운 변경 목록(“델타”)을 생성하는 새로운Differ클래스가 포함되어 있습니다. 또한,ndiff`와 :func:()!restore`라는 두 개의 제너레이터 함수가 있는데, 각각은 두 시퀀스로부터 델타를 반환하거나, 델타로부터 원본 시퀀스 중 하나를 반환합니다. (David Goodger 기여; Tim Peters의 ndiff.py 코드를 기반으로 제너레이터 기능을 구현함.)string모듈에 새로운 상수ascii_letters,ascii_lowercase, 및ascii_uppercase`가 추가되었습니다. 이전에는 여러 표준 라이브러리 모듈에서 A-Za-z 범위의 의미로 :const:!string.letters`를 사용했지만, 로케일이 사용되는 경우 이 가정은 잘못되었습니다. 왜냐하면string.letters`는 현재 로케일에 정의된 유효 문자 세트에 따라 달라지기 때문입니다. 버그가 있는 모듈들은 모두 대신 :const:!ascii_letters`를 사용하도록 수정되었습니다. (발견자 알 수 없음; Fred L. Drake, Jr. 수정함.)mimetypes모듈은 이제 파싱할 파일 이름 목록을 받는MimeTypes클래스가 추가되어 대체 MIME 유형 데이터베이스 사용이 더 쉬워졌습니다. (Fred L. Drake, Jr. 기여)threading모듈에 미래의 특정 시간에 활동을 예약할 수 있는Timer클래스가 추가되었습니다. (Itamar Shtull-Trauring 기여)
인터프리터 변경 및 수정 사항¶
일부 변경 사항은 Python 확장 모듈을 작성하거나, 인터프리터를 임베딩하거나, 또는 단순히 인터프리터 자체를 해킹하는 사람들에게만 영향을 미치므로, C 수준에서 Python 인터프리터를 다루는 경우에만 관련됩니다. 만약 파이썬 코드를만 작성한다면, 여기에 설명된 변경 사항들은 당신에게 크게 영향을 미치지 않을 것입니다.
프로파일링 및 추적 함수를 이제 C에서 구현할 수 있으며, 이는 Python 기반 함수보다 훨씬 높은 속도로 작동하여 프로파일링 및 추적의 오버헤드를 줄여줄 것입니다. 이 기능은 Python 개발 환경 작성자들에게 흥미로울 것입니다. Python API에 두 개의 새로운 C 함수,
PyEval_SetProfile()및PyEval_SetTrace()가 추가되었습니다. 기존의sys.setprofile()및sys.settrace()함수는 여전히 존재하며, 단순히 새로운 C 수준 인터페이스를 사용하도록 변경되었을 뿐입니다. (Fred L. Drake, Jr. 기여)Python 디버거 및 개발 도구 구현자들에게 주로 흥미로운 또 다른 저수준 API가 추가되었습니다. :c:func:`PyInterpreterState_Head`와 :c:func:`PyInterpreterState_Next`는 호출자가 존재하는 모든 인터프리터 객체를 순회할 수 있게 하며; :c:func:`PyInterpreterState_ThreadHead`와 :c:func:`PyThreadState_Next`는 주어진 인터프리터의 모든 스레드 상태를 반복(loop)하는 것을 가능하게 합니다. (David Beazley 기여)
가비지 컬렉터에 대한 C 수준 인터페이스가 확장 유형을 작성하고 함수 오용을 디버깅하기 쉽게 변경되었습니다. 다양한 함수들은 다소 다른 의미론(semantics)을 가지고 있어서, 많은 함수들이 이름을 다시 지정해야 했습니다. 이전 API를 사용하는 확장은 여전히 컴파일되지만 가비지 컬렉션에 참여하지 않으므로, 2.2용으로 업데이트하는 것이 상당히 높은 우선순위로 고려되어야 합니다.
확장 모듈을 새 API로 업그레이드하려면 다음 단계를 수행하십시오:
Py_TPFLAGS_GC를Py_TPFLAGS_HAVE_GC로 이름을 변경하십시오.PyObject_GC_New()또는 :c:func:`PyObject_GC_NewVar`를 사용하여객체를 할당하고, :c:func:`PyObject_GC_Del``을 사용하여 해제하십시오.
PyObject_GC_Init()를PyObject_GC_Track()로, 그리고PyObject_GC_Fini()를PyObject_GC_UnTrack()으로 이름을 변경하십시오.객체 크기 계산에서 :c:macro:`!PyGC_HEAD_SIZE``를 제거하십시오.
PyObject_AS_GC()와PyObject_FROM_GC()에 대한 호출을 제거하십시오.새로운
et포맷 시퀀스가PyArg_ParseTuple()에 추가되었습니다.et은 매개변수와 인코딩 이름을 모두 받으며, 매개변수가 유니코드 문자열인 경우 해당 인코딩으로 변환하거나, 8비트 문자열이라면 원하는 인코딩에 이미 있다고 간주하고 그대로 둡니다. 이는 8비트 문자열이 Python의 기본 ASCII 인코딩에 있음을 가정하고 지정된 새 인코딩으로 변환하는es포맷 문자와는 다릅니다. (M.-A. Lemburg 기여, 후속 섹션에서 설명하는 Windows용 MBCS 지원에 사용됨.)더 간단하고 추정상 더 빠른 다른 인자 구문 분석 함수인
PyArg_UnpackTuple()가 추가되었습니다. 서식 문자열을 지정하는 대신, 호출자는 단순히 예상되는 최소 및 최대 인자 개수와 인자 값으로 채워질 PyObject* 변수의 포인터 집합만 제공하면 됩니다.두 가지 새로운 플래그
METH_NOARGS와METH_O가 메서드 정의 테이블에서 사용 가능하며, 인자가 없거나 단일한 비타입 인자를 가진 메서드의 구현을 단순화합니다. 이러한 메서드를 호출하는 것은METH_VARARGS를 사용하는 해당 메서드를 호출하는 것보다 더 효율적입니다. 또한, 기존의METH_OLDARGS방식의 C 메서드 작성법은 이제 공식적으로 사용 중지되었습니다.두 가지 새로운 래퍼 함수인
PyOS_snprintf()와PyOS_vsnprintf()가 상대적으로 새로운 C 라이브러리 API인snprintf()및vsnprintf()에 대한 크로스플랫폼 구현을 제공하기 위해 추가되었습니다. 표준sprintf()및vsprintf()함수와 달리, Python 버전은 버퍼 오버런 방지를 위해 사용되는 버퍼의 경계(bounds)를 확인합니다. (M.-A. Lemburg 기여.)_PyTuple_Resize()함수가 사용되지 않는 매개변수를 잃어버렸기 때문에, 이제 이전에는 3개의 매개변수를 받던 것과 달리 2개의 매개변수만 받습니다. 세 번째 인자는 한 번도 사용된 적이 없으므로, 이전 버전을 Python 2.2로 포팅할 때 단순히 무시해도 됩니다.
기타 변경 사항 및 수정 사항¶
평소와 같이 소스 트리 전반에 걸쳐 여러 개선 및 버그 수정이 있었습니다. CVS 변경 로그 검색 결과, Python 2.1과 2.2 사이에 527개의 패치가 적용되고 683개의 버그가 수정되었으며; 2.2.1에서는 139개의 패치를 적용하고 143개의 버그를 수정했고; 2.2.2에서는 106개의 패치를 적용하고 82개의 버그를 수정했습니다. 위 수치는 과소평가일 가능성이 높습니다.
눈에 띄는 몇 가지 변경 사항은 다음과 같습니다:
Jack Jansen이 유지 관리하는 Python용 MacOS 포트 코드가 이제 주 Python CVS 트리에서 관리되며, MacOS X를 지원하기 위해 많은 변경 사항들이 이루어졌습니다.
가장 중요한 변화는 Python을 프레임워크로 빌드할 수 있게 된 것입니다. 이는 Python 컴파일 시 configure 스크립트에
--enable-framework옵션을 제공하여 활성화됩니다. Jack Jansen에 따르면, “이것은 자급자족적인 Python 설치와 OS X 프레임워크 “glue”를/Library/Frameworks/Python.framework(또는 선택한 다른 위치)에 설치합니다. 현재로서는 이의 즉각적인 추가 이점은 거의 없습니다 (실제로는 Python을 찾기 위해 PATH를 변경해야 하는 단점이 있습니다). 하지만 이는 기능을 갖춘 Python 애플리케이션 생성, MacPython IDE 포팅, 표준 OSA 스크립팅 언어로서의 Python 사용 등 훨씬 더 많은 것을 위한 기반이 됩니다.”윈도우링, QuickTime, 스크립팅 등에 인터페이스하는 MacPython 툴박스 모듈 대부분은 OS X로 포팅되었지만, :file:`setup.py`에 주석 처리된 상태로 남아 있습니다. 이 모듈들을 실험해 보고 싶은 사용자는 수동으로 주석을 제거할 수 있습니다.
이제 해당 키워드 인수를 받지 않는 내장 함수에 전달된 키워드 인수는 “function takes no keyword arguments” 메시지와 함께
TypeError예외를 발생시킵니다.약한 참조(Weak references)는 Python 2.1에서 확장 모듈로 추가되었으나, 이제 새로운 스타일의 클래스 구현에 사용되면서 핵심 일부가 되었습니다. 따라서
ReferenceError예외는weakref모듈에서 내장 예외로 이동했습니다.새 스크립트인,
Tools/scripts/cleanfuture.py`는 Tim Peters가 작성했으며, 파이썬 소스 코드에서 더 이상 사용되지 않는 ``__future__`구문을 자동으로 제거합니다.추가적인 flags 인자가 내장 함수
compile`에 추가되어, 이제 IDLE 및 기타 개발 환경에서 제공되는 시뮬레이션 셸에서 ``__future__`()구문의 동작을 올바르게 관찰할 수 있게 되었습니다. 이것은 :pep:`264`에 설명되어 있습니다. (Michael Hudson 기여.)Python 1.6에서 도입된 새로운 라이센스는 GPL과 호환되지 않았습니다. 이 문제는 2.2 라이센스에 대한 몇 가지 사소한 텍스트 변경을 통해 수정되었으므로, 이제 Python을 GPL로 보호되는 프로그램 내부에 다시 포함하는 것이 합법적입니다. 참고로 Python 자체는 GPL이 아니며, 항상 그러했듯이 기본적으로 BSD 라이센스와 본질적으로 동등한 라이센스 하에 있습니다. 라이센스 변경 사항은 Python 2.0.1 및 2.1.1 릴리스에도 적용되었습니다.
Windows에서 유니코드 파일 이름이 제시되면, 파이썬은 이제 이를 Microsoft 파일 API에서 사용되는 MBCS 인코딩 문자열로 변환할 것입니다. MBCS가 파일 API에 명시적으로 사용되기 때문에, 기본 인코딩으로 ASCII를 선택한 것은 불편함을 초래합니다. Unix에서는 `locale.nl_langinfo(CODESET)`이 사용 가능하다면 로케일의 문자 세트가 사용됩니다. (Windows 지원은 Mark Hammond가 Marc-André Lemburg의 도움을 받아 기여했습니다. Unix 지원은 Martin von Löwis가 추가했습니다.)
대용량 파일 지원이 이제 Windows에서 활성화되었습니다. (Tim Peters 기여.)
Tools/scripts/ftpmirror.py스크립트는 이제 사용자에게 해당 파일을 가지고 있다면,.netrc파일을 구문 분석합니다. (Mike Romberg 기여.)xrange함수가 반환하는 객체의 일부 기능이 이제 폐지되었으며, 접근할 때 경고를 발생시키며; Python 2.3에서는 사라질 것입니다.xrange()객체는 슬라이싱, 시퀀스 곱셈 및in연산자를 지원하여 전체 시퀀스 유형인 척하려 했으나, 이러한 기능들은 거의 사용되지 않아 버그가 있었습니다. 또한tolist()메서드와start,stop, 그리고step속성들도 폐지되고 있습니다. C 수준에서,PyRange_New()함수의 네 번째 인자인repeat도 폐지되었습니다.딕셔너리 구현에는 여러 패치들이 있었는데, 대부분은 딕셔너리가 해시 값을 은밀하게 변경한 객체를 포함하거나 자신이 포함된 딕셔너리를 변형하는 경우 발생할 수 있는 잠재적인 코어 덤프를 수정하기 위한 것이었습니다. 한동안 python-dev는 Michael Hudson이 코어를 덤프하는 사례를 발견하고, Tim Peters가 버그를 수정하며, Michael이 또 다른 사례를 발견하는 부드러운 리듬을 따랐고, 이는 계속 반복되었습니다.
Windows에서 Python은 Stephen Hansen이 기여한 여러 패치 덕분에 Borland C로 컴파일될 수 있게 되었지만, 그 결과는 아직 완전하게 작동하지는 않습니다. (하지만 이것은 진전 입니다…)
또 다른 Windows 개선 사항: Wise Solutions가 PythonLabs에게 InstallerMaster 8.1 시스템 사용을 아낌없이 제공했습니다. 이전의 PythonLabs Windows 설치 프로그램은 시대에 뒤떨어지기 시작한 Wise 5.0a를 사용했습니다. (Tim Peters가 패키징했습니다.)
pyw로 끝나는 파일은 이제 Windows에서 가져올 수 있습니다..pyw는 Windows 전용이며, 출력을 표시하기 위해 DOS 콘솔이 나타나는 것을 방지하려면 PYTHON.EXE 대신 PYTHONW.EXE를 사용하여 스크립트를 실행해야 함을 나타내는 데 사용됩니다. 이 패치는 해당 스크립트가 모듈로도 사용 가능할 경우에 대비하여 가져오는 것이 가능하게 만듭니다. (David Bolen 구현.)Python이 확장 모듈을 로드하기 위해 C
dlopen()함수를 사용하는 플랫폼에서는, 이제sys.getdlopenflags()및sys.setdlopenflags()함수를 사용하여 :c:func:`dlopen`에 의해 사용되는 플래그를 설정하는 것이 가능합니다. (Bram Stolk 기여.)pow()내장 함수는 부동 소수점 숫자가 제공될 때 더 이상 3개의 인자를 지원하지 않습니다.pow(x, y, z)는(x**y) % z를 반환하지만, 이는 부동 소수점 숫자에는 전혀 유용하지 않으며 최종 결과가 플랫폼에 따라 예측 불가능하게 달라집니다.pow(2.0, 8.0, 7.0)와 같은 호출은 이제TypeError예외를 발생시킬 것입니다.
감사의 글¶
제 저자는 이 글의 다양한 초안을 제안하고 수정하며 도움을 주신 다음 분들께 감사드립니다: Fred Bremmer, Keith Briggs, Andrew Dalke, Fred L. Drake, Jr., Carel Fellinger, David Goodger, Mark Hammond, Stephen Hansen, Michael Hudson, Jack Jansen, Marc-André Lemburg, Martin von Löwis, Fredrik Lundh, Michael McLay, Nick Mathewson, Paul Moore, Gustavo Niemeyer, Don O’Donnell, Joonas Paalasma, Tim Peters, Jens Quade, Tom Reinhardt, Neil Schemenauer, Guido van Rossum, Greg Ward, Edward Welbourne.