Python

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 release)”으로 볼 수 있습니다. 제너레이터와 이터레이터 같은 완전히 새로운 기능도 있지만, 중요하고 영향력이 큰 대부분의 변경 사항은 언어 설계의 불규칙한 부분과 복잡한 구석을 정리하는 데 중점을 두고 있습니다.

이 문서는 새로운 기능에 대한 완전한 사양을 제공하기보다는 편리한 개요를 제공합니다. 자세한 내용은 Python Library ReferencePython Reference Manual 와 같은 Python 2.2 문서를 참조하십시오. 특정 변경 사항에 대한 전체 구현 및 설계 근거를 이해하려면 해당 기능의 PEP를 참고하십시오.

PEP 252 및 253: 타입 및 클래스 변경

Python 2.2에서 가장 크고 광범위한 변화는 Python의 객체 및 클래스 모델에 관한 것입니다. 이러한 변경 사항은 하위 호환성을 유지하도록 설계되었으므로, 기존 코드가 수정 없이도 계속 실행될 가능성이 높지만 동시에 놀라운 새로운 기능들을 제공합니다. 이 문서에서 가장 길고 복잡한 섹션이 시작되기 전에, 변경 사항에 대한 개요와 몇 가지 의견을 제시하겠습니다.

오래전 저는 Python 설계의 결함들을 나열한 웹 페이지를 작성한 적이 있습니다. 가장 중요한 결함 중 하나는 C로 구현된 Python 타입을 서브 클래스화하는 것이 불가능하다는 점이었습니다. 특히 내장 타입(built-in types)을 서브 클래스화할 수 없기 때문에, 예를 들어 리스트에 유용한 메서드 하나를 추가하기 위해 리스트를 서브 클래스화하는 것조차 불가능했습니다. UserList 모듈은 리스트의 모든 메서드를 지원하며 추가로 서브 클래스화가 가능한 클래스를 제공하지만, 일반적인 Python 리스트를 기대하는 많은 C 코드들이 UserList 인스턴스를 수용하지 않습니다.

Python 2.2는 이 문제를 해결하며 그 과정에서 흥미로운 새로운 기능들을 추가합니다. 간단한 요약은 다음과 같습니다.

  • 리스트나 심지어 정수와 같은 내장 타입을 서브 클래스화할 수 있으며, 여러분의 서브 클래스는 원래 타입을 요구하는 모든 곳에서 정상적으로 작동해야 합니다.

  • 이전 버전의 Python에서 제공되던 인스턴스 메서드 외에도 이제 스태틱(static) 메서드와 클래스 메서드를 정의할 수 있습니다.

  • 또한 properties 라는 새로운 메커니즘을 사용하여 인스턴스 어트리뷰트에 액세스하거나 설정할 때 메서드를 자동으로 호출하는 것도 가능합니다. __getattr__() 의 많은 활용 사례를 대신 프로퍼티(property)를 사용하도록 재작성하면 결과 코드가 더 단순해지고 속도도 빨라집니다. 부수적인 장점으로, 이제 어트리뷰트도 독스트링을 가질 수 있습니다.

  • 인스턴스가 가질 수 있는 유효한 어트리뷰스 목록을 slots 를 사용하여 특정 세트로 제한할 수 있으며, 이를 통해 오타를 방지하고 향후 Python 버전에서 더 많은 최적화가 가능해질 수 있습니다.

일부 사용자들은 이러한 변화들에 대해 우려를 표하기도 합니다. 새로운 기능들이 멋지고 이전 버전에서는 불가능했던 다양한 기법들을 가능하게 하지만, 동시에 언어를 더 복잡하게 만든다는 것입니다. 어떤 이들은 Python을 항상 단순함 때문에 추천해 왔으며, 그 단순성이 사라지고 있다고 느낀다고 말합니다.

개인적으로 저는 걱정할 필요가 없다고 생각합니다. 새로운 기능 중 상당수는 꽤 난해하며(esoteric), 이를 알지 못해도 많은 양의 Python 코드를 작성할 수 있습니다. 단순한 클래스를 작성하는 것은 예전보다 어렵지 않으므로, 실제로 필요한 경우가 아니면 이 기능들을 학습하거나 가르치는 데 신경 쓸 필요가 없습니다. 이전에는 C에서만 가능했던 매우 복잡한 작업들이 이제 순수 Python으로도 가능해졌으며, 제 생각에 이는 모두 긍정적인 변화입니다.

새로운 기능이 작동하는 데 필요한 모든 세부 사례와 사소한 변경 사항을 하나하나 다 다루지는 않겠습니다. 대신 이 섹션에서는 전체적인 개요를 설명할 것입니다. Python 2.2의 새로운 객체 모델에 대한 더 자세한 정보는 관련 링크, “관련 링크” 섹션을 참조하십시오.

구형(Old) 및 신형(New) 클래스

먼저, Python 2.2에는 실제로 두 가지 종류의 클래스가 있음을 알아야 합니다. 하나는 고전(classic) 또는 구형 스타일(old-style) 클래스이고, 다른 하나는 신형(new-style) 클래스입니다. 구형 스타일 클래스 모델은 이전 버전의 Python과 정확히 동일합니다. 이 섹션에서 설명하는 모든 새로운 기능은 신형 스타일 클래스에만 적용됩니다. 이러한 분리는 영구적으로 유지될 의도가 아니며, 머지않아 (아마도 Python 3.0에서) 구형 스타일 클래스는 제거될 것입니다.

그렇다면 신형 스타일 클래스는 어떻게 정의할까요? 기존의 신형 스타일 클래스를 서브 클래스화함으로써 정의합니다. 정수, 리스트, 딕셔너리, 심지어 파일에 이르기까지 대부분의 Python 내장 타입이 이제는 신형 스타일 클래스입니다. 모든 내장 타입의 기본 클래스인 object 라는 이름의 신형 스타일 클래스도 추가되었으므로, 적합한 내장 타입이 없다면 단순히 object 를 서브 클래스화하면 됩니다.

class C(object):
    def __init__ (self):
        ...
    ...

이는 Python 2.2에서 베이스 클래스가 없는 class 문이 항상 고전 스타일(classic) 클래스로 간주됨을 의미합니다. (실제로 모듈 수준의 변수인 __metaclass__ 를 설정하여 이를 변경할 수도 있지만, 상세 내용은 PEP 253 을 참조하십시오. 하지만 그냥 object 를 서브 클래스화하는 것이 더 간편합니다.)

내장 타입에 대한 형(type) 객체들은 기발한 기법을 사용하여 내장형으로 제공됩니다. Python에는 언제나 int(), float(), str() 라는 이름의 내장 함수가 있었습니다. 2.2 버전에서 이들은 더 이상 함수가 아니며, 호출될 때 팩토리(factory)처럼 동작하는 형 객체입니다.

>>> 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() 메서드를 추가한 클래스가 포함되어 있었으나, 이 클래스는 내장 파일을 기대하는 내부 함수에 전달될 수 없었습니다. 이제 우리의 새로운 LockableFile 을 사용하면 이를 처리할 수 있습니다.

디스크립터(Descriptors)

이전 버전의 Python에서는 객체가 어떤 어트리뷰스와 메서드를 지원하는지 파악할 일관된 방법이 없었습니다. 이름들의 리스트인 __members__`와 :attr:!__methods__`를 정의하는 등의 비공식적인 관습은 있었으나, 확장형이나 클래스의 작성자가 이를 정의하는 번거로움을 감수하지 않는 경우가 많았습니다. 객체의 __dict__`를 조사하는 방식을 사용할 수도 있었지만, 클래스 상속이나 임의의 :meth:!__getattr__` 훅이 사용되는 경우에는 여전히 정확하지 않을 수 있었습니다.

새로운 클래스 모델의 핵심 아이디어는 descriptors 를 사용하여 객체의 어트리뷰스를 기술하는 API가 정형화되었다는 것입니다. 디스크립터는 해당 어트리뷰스가 메서드인지 필드인지를 명시하며 그 값을 정의합니다. 이 디스크립터 API를 통해 스태틱 메서드와 클래스 메서드를 포함한 더 다양하고 특수한 구조들을 구현할 수 있게 되었습니다.

어트리뷰스 디스크립터는 클래스 객체 내부에 존재하는 객체이며, 다음과 같은 몇 가지 어트리뷰스를 가집니다.

  • __name__ 은 해당 어트리뷰스의 이름입니다.

  • __doc__ 은 해당 어트리뷰스의 독스트링입니다.

  • __get__(object)object 로부터 어트리뷰스 값을 가져오는 메서드입니다.

  • __set__(object, value)object 의 어트리뷰스를 value 로 설정합니다.

  • __delete__(object, value)objectvalue 어트리뷰스를 삭제합니다.

예를 들어, 여러분이 obj.x 라고 쓸 때 파이썬이 실제로 수행하는 단계는 다음과 같습니다.

descriptor = obj.__class__.x
descriptor.__get__(obj)

메서드의 경우, descriptor.__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 및 properties와 같은 다른 새로운 기능들도 새로운 종류의 디스크립터로 구현되었으며, 독창적인 기능을 수행하는 디스크립터 클래스를 작성하는 것은 어렵지 않습니다. 예를 들어, 메서드에 대해 에이펠(Eiffel) 스타일의 전제 조건과 후속 조건을 작성할 수 있게 해주는 디스크립터 클래스를 만드는 것이 가능합니다. 이 기능을 사용하는 클래스는 다음과 같이 정의될 수 있습니다.

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

고전 클래스에 대한 조회 규칙은 단순하지만 그리 똑똑하지는 않습니다. 베이스 클래스가 왼쪽에서 오른쪽으로 깊이 우선 탐색(depth-first)됩니다. D.save() 를 참조하면 클래스 D, B, 그리고 그 다음 A 를 탐색하며, 이 과정에서 save() 가 발견되어 반환됩니다. C.save() 는 아예 찾을 수 없습니다. 이는 문제인데, 만약 Csave() 메서드가 C 에 특화된 어떤 내부 상태를 저장하는 것이라면, 이를 호출하지 못해 해당 상태가 저장되지 않는 결과로 이어지기 때문입니다.

신형 스타일 클래스는 설명하기는 조금 더 복잡하지만 이 상황에서 올바르게 작동하는 다른 알고리즘을 따릅니다. (참고: Python 2.3에서는 대부분의 경우 동일한 결과를 생성하면서도 매우 복잡한 상속 구조에서 더 유용한 결과를 제공하는 방식으로 이 알고리즘을 변경했습니다.)

  1. 고전 조회 규칙을 따라 모든 베이스 클래스를 나열하고 방문할 때마다 중복되는 경우 여러 번 포함합니다. 위 예시에서 방문한 클래스 목록은 [D, B, A, C, A 입니다.]

  2. 목록에서 중복된 클래스를 스캔합니다. 중복이 발견되면 마지막 하나만 남기고 나머지를 제거합니다. 위 예시에서 중복을 제거한 후 목록은 [D, B, C, A]가 됩니다.

이 규칙을 따르면 D.save`를 참조할 우리가 원하는 동작인 :meth:()!C.save`가 반환됩니다. 이 조회 규칙은 Common Lisp에서 따르는 것과 동일합니다. 새로운 내장 함수인 super`는 파이썬의 알고리즘을 재구현할 필요 없이 클래스의 상위 클래스(superclass)에 접근하는 방법을 제공합니다. 가장 일반적으로 사용되는 형태는 ``super(class, obj)``이며, 이는 바인딩된 상위 클래스 객체(실제 클래스 객체가 아님)를 반도입니다. 형태는 메서드 내에서 상위 클래스의 메서드를 호출할 사용됩니다. 예를 들어, :class:()!D`의 save() 메서드는 다음과 같이 보일 것입니다.

class D (B,C):
    def save (self):
        # 상위 클래스의 .save() 호출
        super(D, self).save()
        # 여기에 D의 개인 정보 저장
        ...

super()super(class) 또는 super(class1, class2) 로 호출될 때 바인딩되지 않은 상위 클래스 객체를 반환할 수도 있지만, 이는 자주 유용하게 쓰이지는 않을 것입니다.

어트리뷰스 액세스

상당수의 정교한 파이썬 클래스들이 __getattr__() 를 사용하여 어트리뷰스 액세스에 대한 훅을 정의합니다. 이는 보통 obj.parent 와 같은 어트리뷰스 액세스를 obj.get_parent 와 같은 메서드 호출로 자동 매핑하여 코드를 더 읽기 쉽게 만들기 위한 편의성을 위해 수행됩니다. Python 2.2는 어트리뷰스 액세스를 제어하는 몇 가지 새로운 방법을 추가했습니다.

첫째로, __getattr__(attr_name) 은 여전히 신형 스타일 클래스에서 지원되며 변화된 것이 없습니다. 이전과 마찬가지로, obj.foo 에 액세스하려고 시도했으나 인스턴스 딕셔너리에서 foo 라는 이름의 어트리뷰스를 찾을 수 없을 때 호출됩니다.

신형 스타일 클래스는 새로운 메서드인 __getattribute__(attr_name) 도 지원합니다. 두 메서스의 차이점은 __getattribute__() 는 어트리뷰스에 액세스할 때마다 항상 호출되는 반면, 기존의 __getattr__() 는 인스턴스 딕셔너리에서 foo 를 찾을 수 없을 때만 호출된다는 것입니다.

하지만, Python 2.2에서 제공하는 properties 지원은 어트리뷰트 참조를 가로채는 더 간단한 방법이 될 수 있습니다. __getattr__() 메서드를 작성하는 것은 복잡한데, 재귀를 방지하기 위해 내부에서 일반적인 어트리뷰트 액세스를 사용할 수 없으며 대신 __dict__ 의 내용과 씨름해야 하기 때문입니다. 또한 __getattr__() 메서드는 Python이 __repr__() 이나 __coerce__() 와 같은 다른 메서드를 확인하는 과정에서도 호출되므로, 이를 염두에 두고 작성해야 합니다. 마지막으로, 모든 어트리뷰트 액세스 시 함수를 호출하면 상당한 성능 손실이 발생합니다.

property`는 어트리뷰트를 가져오고(get), 설정하고(set), 삭제하는(delete) 가지 기능과 도큐스트링을 하나로 묶은 새로운 내장 타입입니다. 예를 들어, 계산되지만 설정도 가능한 :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,
                    "이 인스턴스의 저장 크기")

이는 모든 다른 어트리뷰트를 인스턴스의 __dict__ 에서 가져오면서 size 어트리뷰트만 확인하여 특별하게 처리하는 두 개의 __getattr__()/__setattr__() 메서드보다 확실히 더 명확하고 쓰기 쉽습니다. 또한, 어트리뷰트 접근 중 함수 호출을 수행해야 하는 것은 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'

__slots__ 에 나열되지 않은 어트리뷰스에 값을 할당하려고 할 때 AttributeError 가 발생하는 것을 확인하십시오.

PEP 234: 이터레이터

2.2에서 추가된 또 다른 중요한 기능은 C와 파이썬 수준 모두에서 제공되는 반복(iteration) 인터페이스입니다. 이제 객체는 호출자가 자신을 루프(loop)로 돌리는 방식을 정의할 수 있습니다.

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] 를 사용하여 여섯 번째 요소를 무작위로 접근할 수 있다는 것을 의미하지는 않지만, 실제로는 그렇게 작동해야 합니다.

Python 2.2에서는 이터레이션이 별도로 구현될 수 있으며, __getitem__() 메서드는 실제 무작위 접근을 지원하는 클래스로 제한할 수 있습니다. 이터레이터의 기본 아이디어는 간단합니다. 새로운 내장 함수인 iter(obj) 또는 iter(C, sentinel) 이 이터레이터를 얻기 위해 사용됩니다. iter(obj) 는 객체 obj 에 대한 이터레이터를 반환하며, iter(C, sentinel) 은 호출 가능한 객체 C 를 실행하다가 sentinel 을 반환하여 이터레이션이 완료되었음을 알릴 때까지 반복하는 이터레이터를 반환합니다.

파이썬 클래스는 객체를 위한 새로운 이터레이터를 생성하고 반환하는 __iter__() 메서드를 정의할 수 있으며, 만약 객체가 스스로의 이터레이터인 경우 이 메서드는 단순히 self 를 반환하면 됩니다. 특히 이터레이터는 보통 자기 자신이 이터레이터입니다. C로 구현된 확장 타입은 이터레이터를 반환하기 위해 tp_iter 함수를 구현할 수 있으며, 이터레이터처럼 동작하려는 확장 타입은 tp_iternext 함수를 정의할 수 있습니다.

그렇다면 이 모든 것 끝에, 이터레이터는 실제로 무엇을 합니까? 이터레이터에는 인수를 받지 않고 다음 값을 반환하는 필수 메서드인 next() 가 있습니다. 더 이상 반환할 값이 없을 때 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)

파이썬의 일부 기본 타입에 이터레이터 지원이 추가되었습니다. 딕셔너리에 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:
    # 각 행에 대해 무언가를 수행
    ...

이터레이터에서는 앞으로만 나아갈 수 있으며, 이전 요소를 가져오거나 이터레이터를 재설정하거나 사본을 만들 수 없다는 점에 유의하십시오. 이터레이터 객체가 그러한 추가 기능을 제공할 수도 있지만, 이터레이터 프로토콜은 next() 메서드만을 요구합니다.

더 보기

PEP 234 - 이터레이터

Ka-Ping Yee와 GvR이 작성하였으며, Python Labs 팀(주로 GvR과 Tim Peters)에 의해 구현되었습니다.

PEP 255: 간단한 제너레이터

제너레이터는 이터레이터의 도입과 상호작용하는 또 다른 새로운 기능입니다.

파이썬이나 C에서 함수 호출이 어떻게 작동하는지 잘 알고 있을 것입니다. 함수를 호출하면 로컬 변수가 생성되는 비공개 이름 공간(namespace)을 할당받습니다. 함수가 return 문에 도달하면 로컬 변수가 소멸되고 결과 값이 호출자에게 반환됩니다. 같은 함수를 나중에 다시 호출하면 새로운 로컬 변수 집합이 생성됩니다. 하지만, 만약 로컬 변수가 함수를 나갈 때 사라지지 않는다면 어떨까요? 나중에 중단했던 지점부터 함수를 재개할 수 있다면 어떨까요? 이것이 제너레이터가 제공하는 기능이며, 제너레이터는 재개 가능한 함수라고 생각할 수 있습니다.

다음은 제너레이터 함수의 가장 간단한 예입니다:

def generate_ints(N):
    for i in range(N):
        yield i

제너레이터를 위해 새로운 키워드인 yield 가 도입되었습니다. yield 문을 포함하는 모든 함수는 제너레이터 함수로 간주되며, 이는 파이썬의 바이트코드 컴파일러에 의해 감지되어 해당 함수를 특별하게 컴파일합니다. 새로운 키워드가 도입되었기 때문에, 모듈 소스 코드 상단에 from __future__ import generators 문을 포함하여 모듈에서 제너레이터를 명시적으로 활성화해야 합니다. 파이썬 2.3에서는 이 문장이 불필요해질 것입니다.

제너레이터 함수를 호출하면 단일 값을 반환하는 대신 이터레이터 프로토콜을 지원하는 제너레이터 객체를 반환합니다. yield 문을 실행할 때, 제너레이터는 return 문과 유사하게 i 의 값을 출력합니다. yieldreturn 문의 큰 차이점은 제너레이터가 yield 에 도달하면 실행 상태가 일시 중단되고 지역 변수가 보존된다는 것입니다. 제너레이터의 next() 메서드가 다음번 호출될 때, 함수는 yield 문 바로 다음부터 실행을 재개합니다. (복잡한 이유로 인해, yield 문은 tryfinally 문 내의 try 블록 안에서 허용되지 않습니다. yield 와 예외 간의 상호작용에 대한 자세한 설명은 PEP 255 를 참조하십시오.)

generate_ints() 제너레이터의 사용 예시입니다:

>>> 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 traversal)하는 기능을 구현합니다.

# 트리 노드를 중위 순회(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

Lib/test/test_generators.py 에 있는 다른 두 가지 예제는 N-여왕 문제(NxN 체스판에서 서로를 위협하지 않도록 N개의 여왕을 배치하는 문제)와 기사의 여행(기사가 NxN 체스판의 모든 칸을 중복 방문 없이 한 번씩 거쳐가는 경로 찾기)에 대한 해결책을 제공합니다.

제너레이터 개념은 다른 프로그래밍 언어, 특히 제너레이터 개념이 중심인 Icon(https://www2.cs.arizona.edu/icon/)에서 유래했습니다. Icon에서는 모든 표현식과 함수 호출이 제너레이터처럼 동작합니다. https://www2.cs.arizona.edu/icon/docs/ipd266.htm의 “An Overview of the Icon Programming Language”에 수록된 예제 하나가 이러한 모습이 어떠한지 보여줍니다:

sentence := "Store it in the neighboring harbor"
if (i := find("or", sentence)) > 5 then write(i)

Icon에서 find() 함수는 부분 문자열 “or”이 발견되는 인덱스인 3, 23, 33을 반환합니다. if 문에서 i 에 처음으로 3이라는 값이 할당되지만, 3은 5보다 작으므로 비교가 실패하고 Icon은 두 번째 값인 23으로 다시 시도합니다. 23은 5보다 크므로 비교가 성공하며 코드는 화면에 23을 출력합니다.

Python은 제너레이터를 핵심 개념으로 채택하는 범위가 Icon만큼 넓지는 않습니다. 제너레이터는 Python 핵심 언어의 새로운 부분으로 간주되지만, 이를 반드시 배우거나 사용할 필요는 없습니다. 해결해야 할 문제가 있다면 무시해도 좋습니다. Icon과 비교했을 때 Python 인터페이스의 참신한 특징 중 하나는 제너레이터의 상태가 다른 함수로 전달하거나 데이터 구조에 저장할 수 있는 구체적인 객체(이터레이터)로 표현된다는 점입니다.

더 보기

PEP 255 - 단순 제너레이터

Neil Schemenauer, Tim Peters, Magnus Lie Hetland이 작성했습니다. 대부분은 Neil Schemenauer와 Tim Peters가 구현했으며, Python Labs 팀이 다른 수정 사항을 반영했습니다.

PEP 237: Long 정수 및 정수의 통합

최근 버전들에서 대부분의 기계에서 32비트 값인 일반 정수와 임의의 크기를 가질 수 있는 long 정수를 구분하는 것이 번거로워졌습니다. 예를 들어, 2**32 바이트 이상의 파일을 지원하는 플랫폼에서는 파일 객체의 tell() 메서드가 long 정수를 반환해야 합니다. 그러나 Python의 여러 부분에서는 일반 정수만을 기대하며, 대신 long 정수가 제공될 경우 오류를 발생시켰습니다. 예를 들어, Python 1.5에서 slice 인덱스로는 일반 정수만 사용할 수 있었으며, 'abc'[1L:] 은 ‘slice index must be int’라는 메시지와 함께 TypeError 예외를 발생시켰습니다.

Python 2.2는 필요한 경우 값을 short에서 long 정수로 변환합니다. 이제 컴파일러가 적절한 유형을 선택하므로 long 정수 리터럴을 나타내기 위해 ‘L’ 접미사가 더 이상 필요하지 않습니다. (‘L’ 접미사 사용은 향후 Python 2.x 버전에서 권장되지 않으며, Python 2.4에서는 경고를 발생시키고, 아마도 Python 3.0에서 삭제될 것입니다.) 기존에 OverflowError 를 발생시키던 많은 연산이 이제 long 정수를 결과로 반환합니다. 예를 들어:

>>> 1234567890123
1234567890123L
>>> 2 ** 64
18446744073709551616L

대부분의 경우 이제 정수와 long 정수는 동일하게 처리됩니다. 내장 함수인 type() 을 사용하여 여전히 구분할 수 있지만, 그런 경우가 드뭅니다.

더 보기

PEP 237 - Long 정수 및 정수의 통합

Moshe Zadka와 Guido van Rossum이 작성했습니다. 대부분은 Guido van Rossum이 구현했습니다.

PEP 238: 나눗셈 연산자 변경

Python 2.2에서 가장 논란이 되었던 변화는 처음부터 존재해 온 오래된 설계 결함을 수정하려는 노력의 시작을 알립니다. 현재 Python의 나눗셈 연산자 / 은 두 개의 정수 인자가 주어질 때 C의 나눗셈 연산자와 유사하게 동작합니다. 즉, 소수 부분이 있을 경우 이를 버림(truncate)하여 정수 결과를 반환합니다. 예를 들어, 3/2 는 1.5가 아닌 1이며, (-1)/2 는 -0.5가 아닌 -1입니다. 이는 나눗셈 결과가 두 피연산자의 유형에 따라 예상치 못하게 달라질 수 있음을 의미하며, Python은 동적 타입 언어이므로 피연산자의 가능한 타입을 결정하기 어려울 수 있습니다.

(이것이 정말로 설계 결함인지, 그리고 이를 고치기 위해 기존 코드를 깨뜨릴 가치가 있는지에 대한 논란이 있었습니다. 이 문제는 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(정수 나눗셈)”은 현재 Python의 / 연산자가 정수 피연산자를 받았을 때 수행하는 방식입니다. 결과는 True division에 의해 반환된 값의 내림(floor) 값이 됩니다. “Classic division(기존 나눗셈)”은 현재 / 의 혼합된 동작을 의미합니다. 피연산자가 모두 정수일 때는 Floor division 결과를 반환하고, 피연산자 중 하나가 부동소수점 수일 때는 True division 결과를 반환합니다.

다음은 2.2에서 도입된 변경 사항입니다:

  • 새로운 연산자 //``는 Floor division 연산자입니다. (네, C++의 주석 기호처럼 보인다는 것을 알고 있습니다.) ``//``는 피연산자의 타입과 상관없이 *항상* Floor division을 수행하므로, ``1 // 2``는 0이고 ``1.0 // 2.0 또한 0.0이 됩니다.

    //``는 Python 2.2에서 항상 사용 가능하며, ``__future__ 문을 사용하여 활성화할 필요가 없습니다.

  • 모듈에 from __future__ import division 을 포함하면 / 연산자가 True division 결과를 반환하도록 변경되어 1/2 가 0.5이 됩니다. __future__ 문이 없으면 / 는 여전히 Classic division을 의미합니다. / 의 기본 의미는 Python 3.0이 되어서야 변경될 것입니다.

  • 클래스는 두 나눗셈 연산자를 오버로드하기 위해 __truediv__()__floordiv__() 메서드를 정의할 수 있습니다. C 수준에서도 PyNumberMethods 구조체에 슬롯이 있어 확장 유형(extension types)도 두 연산자를 정의할 수 있습니다.

  • Python 2.2는 변경된 나눗셈 의미와 함께 코드가 작동하는지 테스트하기 위한 일부 명령줄 인수를 지원합니다. python`을 :option:!-Q warn`으로 실행하면 정수 두 개에 나누기가 적용될 때 경고가 발생합니다. 이를 사용하여 변경 사항의 영향을 받는 코드를 찾고 수정할 수 있습니다. 기본적으로 Python 2.2는 경고 없이 단순히 클래식 나눗셈을 수행하며, 이 경고는 Python 2.3에서 기본으로 활성화됩니다.

더 보기

PEP 238 - 나눗셈 연산자 변경

Moshe Zadka와 Guido van Rossum이 작성했습니다. Guido van Rossum이 구현했습니다.

유니코드 변경 사항

Python 2.2에서 파이썬의 유니코드 지원이 약간 개선되었습니다. 유니코드 문자열은 보통 16비트 부호 없는 정수인 UCS-2로 저장됩니다. Python 2.2는 구성 스크립트에 --enable-unicode=ucs4`를 제공함으로써 내부 인코딩으로 32비트 부호 없는 정수인 UCS-4를 사용하도록 컴파일할 수도 있습니다. (또한 :option:!–disable-unicode`를 지정하여 유니코드 지원을 완전히 비활성화할 수도 있습니다.)

When built to use UCS-4 (a “wide Python”), the interpreter can natively handle Unicode characters from U+000000 to U+110000, so the range of legal values for the unichr() function is expanded accordingly. Using an interpreter compiled to use UCS-2 (a “narrow Python”), values greater than 65535 will still cause unichr() to raise a ValueError exception. This is all described in PEP 261, “Support for ‘wide’ Unicode characters”; consult it for further details.

또 다른 변경 사항은 설명하기가 더 간단합니다. 도입 이후 유니코드 문자열은 UTF-8 또는 Latin-1과 같은 선택된 인코딩으로 변환하는 encode() 메서드를 지원해 왔습니다. 2.2에서는 8비트 문자열에 대칭적인 decode([*encoding*]) 메서드가 추가되었습니다(유니코드 문자열에는 해당 사항 없음). 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'

클래스 인스턴스를 유니코드로 변환하려면 __str__`와 유사하게 클래스에 :meth:()!__unicode__` 메서드를 정의할 수 있습니다.

encode(), decode(), 및 __unicode__() 는 Marc-André Lemburg가 구현했습니다. 내부적으로 UCS-4를 지원하기 위한 변경 사항은 Fredrik Lundh와 Martin von Löwis이 구현했습니다.

더 보기

PEP 261 - ‘wide’ 유니코드 문자 지원

Paul Prescod이 작성했습니다.

PEP 227: 중첩된 스코프

Python 2.1에서 정적으로 중첩된 스코프가 선택적 기능으로 추가되었으며, from __future__ import nested_scopes 지시어로 활성화할 수 있었습니다. 2.2에서는 중첩된 스코프를 특별히 활성화할 필요 없이 항상 존재합니다. 이 섹션의 나머지 부분은 “What’s New in Python 2.1” 문서에 있는 중첩된 스코프 설명의 복사본이며, 2.1이 출시되었을 때 읽어보셨다면 이 섹션의 나머지는 건너뛰어도 좋습니다.

Python 2.1에서 도입되고 2.2에서 완성된 가장 큰 변화는 Python의 스코프 규칙입니다. Python 2.0에서는 어떤 시점에서든 변수 이름을 찾기 위해 사용되는 네임스페이스가 많아야 세 개(로컬, 모듈 레벨, 내장 네임스페이스)였습니다. 이는 사람들의 직관적인 기대와 일치하지 않아 종종 당혹감을 주었습니다. 예를 들어, 중첩된 재귀 함수 정의가 작동하지 않습니다:

def f():
    ...
    def g(value):
        ...
        return g(value-1) + 1
    ...

함수 g() 은 이름 g 의 바인딩이 로컬 네임스페이스나 모듈 수준의 네임스페이스 어디에도 존재하지 않기 때문에 항상 NameError 예외를 발생시킵니다. 이는 실제 상황에서는 큰 문제가 아닐 수 있지만(이런 식으로 내부 함수를 재귀적으로 정의하는 경우가 얼마나 자주 있겠습니까?), 또한 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

그 결과, 강한 기능적 스타일로 작성된 파이썬 코드의 가독성이 크게 저하되었습니다.

파이썬 2.2에서 가장 중요한 변화는 이 문제를 해결하기 위해 언어에 정적 스코핑(static scoping)이 추가되었다는 점입니다. 그 첫 번째 결과로, 위 예제에서 name=name 과 같은 기본 인자가 더 이상 필요하지 않게 되었습니다. 간단히 말해, 특정 변수 이름이 함수 내에서 할당되거나(대입 또는 def, class, import 문에 의해) 값이 할당되지 않은 경우, 해당 변수에 대한 참조는 이를 둘러싼 범위(enclosing scope)의 로컬 네임스페이스에서 검색됩니다. 규칙에 대한 더 자세한 설명과 구현 방식에 대한 분석은 PEP에서 확인할 수 있습니다.

이 변화는 동일한 변수 이름이 모듈 수준과 하위 함수 정의를 포함하는 함수의 로컬 변수로 모두 사용되는 코드에서 일부 호환성 문제를 일으킬 수 있습니다. 그러나 이러한 코드는 처음부터 읽기 상당히 혼란스러웠을 것이므로 실제 발생 가능성은 낮아 보입니다.

이 변화의 부작용 중 하나는 특정 조건에서 함수 스코프 내의 from module import *exec 구문이 금지되었다는 것입니다. 파이썬 참조 매뉴얼은 항상 from module import * 가 모듈의 최상위 수준에서만 허용된다고 명시해 왔지만, CPython 인터프리터는 이전까지 이를 강제한 적이 없었습니다. 중첩된 스코프 구현의 일부로, 파이썬 소스를 바이트코드로 변환하는 컴파일러는 포함된 스코프(containing scope)의 변수에 접근하기 위해 다른 코드를 생성해야 합니다. from module import *exec 는 컴파일 시점에 알 수 없는 이름을 로컬 네임스페이스에 추가하므로, 컴파일러가 이를 판단하는 것을 불가능하게 만듭니다. 따라서 함수 내에 자유 변수가 포함된 함수 정의나 lambda 표현식이 있는 경우, 컴파일러는 SyntaxError 예외를 발생시켜 이를 알립니다.

앞선 설명을 조금 더 명확하게 설명하기 위한 예시입니다:

x = 1
def f():
    # 다음 줄은 구문 오류입니다
    exec 'x=2'
    def g():
        return x

exec 문이 포함된 4행은 구문 오류입니다. 왜냐하면 execg() 에 의해 접근되어야 하는 값과 이름이 같은 새로운 로컬 변수 x 를 정의하게 되기 때문입니다.

대부분의 파이썬 코드에서 exec 는 거의 사용되지 않으므로(사용되더라도 대개 설계가 부실하다는 신호입니다), 이것이 큰 제한 사항이 되지는 않을 것입니다.

더 보기

PEP 227 - 정적으로 중첩된 스코프

Jeremy Hylton이 작성하고 구현했습니다.

개선 및 신규 모듈

  • xmlrpclib 모듈은 Fredrik Lundh가 표준 라이브러리에 기여한 것으로, XML-RPC 클라이언트를 작성하는 기능을 지원합니다. XML-RPC는 HTTP와 XML을 기반으로 구축된 단순한 원격 프로시저 호출(RPC) 프로토콜입니다. 예를 들어, 다음 코드 조각은 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': 'A utility which converts HTML to XSL FO.',
    #   'title': 'html2fo 0.3 (Default)'}, ... ]
    

    SimpleXMLRPCServer 모듈을 사용하면 간단한 XML-RPC 서버를 쉽게 만들 수 있습니다. XML-RPC에 대한 더 자세한 정보는 http://xmlrpc.scripting.com/에서 확인하십시오.

  • 새로운 hmac 모듈은 RFC 2104 에 기술된 HMAC 알고리즘을 구현합니다. (Gerhard Häring이 기여함.)

  • 기본적으로 긴 튜플을 반환하던 여러 함수가 이제는 튜플처럼 동작하면서도 memberst_mtime 또는 tm_year 와 같은 기억하기 쉬운 속성을 갖는 유사 시퀀스(pseudo-sequences)를 반환합니다. 개선된 함수에는 os 모듈의 stat(), fstat(), statvfs(), fstatvfs()time 모듈의 localtime(), gmtime(), 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를 지원하도록 컴파일될 수 있습니다. 파이썬의 configure 스크립트에 --enable-ipv6 옵션을 지정하십시오. (Jun-ichiro “itojun” Hagino 기여.)

  • C long long 타입을 지원하는 플랫폼에서 64비트 정수를 처리하기 위해 struct 모듈에 두 개의 새로운 포맷 문자가 추가되었습니다. q 은 부호 있는 64비트 정수용이며, Q 는 부호 없는 64비트 정수용입니다. 값은 파이썬의 long 정수 타입으로 반환됩니다. (Tim Peters 기여.)

  • 인터프리터의 대화형 모드에서는 파이썬 2.1에서 도입된 pydoc 모듈을 사용하여 대화형 도움말을 제공하는 새로운 내장 함수 help() 가 있습니다. help(object)object 에 대한 사용 가능한 모든 도움말 텍스트를 표시합니다. 인수가 없는 help() 를 호출하면 온라인 도움말 유틸리티로 진입하며, 여기서 함수, 클래스 또는 모듈의 이름을 입력하여 관련 도움말을 읽을 수 있습니다. (Guido van Rossum이 Ka-Ping Yee의 pydoc 모듈을 사용하여 기여함.)

  • re 모듈의 기반이 되는 SRE 엔진에 다양한 버그 수정 및 성능 개선이 이루어졌습니다. 예를 들어, re.sub()re.split() 함수가 C로 재작성되었습니다. 또 다른 기여 패치는 특정 유니코드 문자 범위의 속도를 두 배로 향상시켰으며, 주어진 문자열 내에서 중복되지 않는 모든 일치 항목에 대한 반복자(iterator)를 반환하는 새로운 finditer() 메서드가 추가되었습니다. (SRE는 Fredrik Lundh가 유지 관리하며, BIGCHARSET 패치는 Martin von Löwis가 기여했습니다.)

  • smtplib 모듈이 이제 RFC 2487, “Secure SMTP over TLS”를 지원하므로 파이썬 프로그램과 메시지를 전달받는 메일 전송 에이전트 사이의 SMTP 트래픽을 암호화할 수 있습니다. 또한 smtplib 은 SMTP 인증도 지원합니다. (Gerhard Häring 기여.)

  • Piers Lauder가 유지 관리하는 imaplib 모듈이 RFC 2342 에 정의된 NAMESPACE 확장과 SORT, GETACL, SETACL 등 여러 새로운 확장을 지원하게 되었습니다. (Anthony Baxter와 Michel Pelletier 기여.)

  • rfc822 모듈의 이메일 주소 구문 분석이 이제 RFC 822 의 업데이트 버전인 RFC 2822 를 준수합니다. (모듈 이름은 rfc2822 로 변경되지 않습니다.) 또한 이메일 메시지를 구문 분석하고 생성하기 위한 새로운 패키지인 email 이 추가되었습니다. (Barry Warsaw가 기여했으며, 그의 Mailman 작업에서 파생된 결과입니다.)

  • difflib 모듈에 두 텍스트 줄 시퀀스 사이의 변경 사항( “delta” )을 사람이 읽기 쉬운 목록으로 생성하는 새로운 Differ 클래스가 포함되었습니다. 또한 두 시퀀스에서 delta를 반환하거나, delta에서 원래 시퀀스 중 하나를 반환하는 두 개의 제너레이터 함수인 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 기여.)

인터프리터 변경 사항 및 수정 사항

일부 변경 사항은 파이썬 확장 모듈을 작성하거나, 인터프리터를 임베딩하거나, 또는 인터프리터 자체를 수정하는 등 C 수준에서 파이썬 인터프리터를 다루는 사용자에게만 영향을 미칩니다. 파이썬 코드만 작성한다면 여기 설명된 내용 중 어떤 것도 큰 영향을 주지 않을 것입니다.

  • 프로파일링 및 추적 함수를 이제 C로 구현할 수 있으며, 이는 파이썬 기반 함수보다 훨씬 빠른 속도로 작동하여 프로파일링과 추적의 오버헤드를 줄여줍니다. 이 기능은 파이썬 개발 환경 제작자들에게 유용할 것입니다. 파이썬 API에 두 개의 새로운 C 함수인 PyEval_SetProfile()PyEval_SetTrace() 가 추가되었습니다. 기존의 sys.setprofile()sys.settrace() 함수는 여전히 존재하며, 단지 새로운 C 수준 인터페이스를 사용하도록 변경되었습니다. (Fred L. Drake, Jr. 기여.)

  • 주로 파이썬 디버거 및 개발 도구 구현자가 관심을 가질 만한 또 다른 저수준 API가 추가되었습니다. PyInterpreterState_Head()PyInterpreterState_Next() 를 통해 호출자는 모든 기존 인터프리터 객체를 순회할 수 있으며, PyInterpreterState_ThreadHead()PyThreadState_Next() 를 사용하여 특정 인터프리터의 모든 스레드 상태를 반복할 수 있습니다. (David Beazley 기여.)

  • 가비지 컬렉션을 지원하는 확장 타입을 더 쉽게 작성하고 기능의 오용을 디버깅하기 용이하도록 가비지 컬렉터에 대한 C 수준 인터페이스가 변경되었습니다. 여러 함수의 의미론(semantics)이 약간씩 달라져 많은 함수가 이름을 변경해야 했습니다. 이전 API를 사용하는 확장은 여전히 컴파일되지만, 가비지 컬렉션에 참여하지 않으므로 2.2 버전에 맞춰 이를 업데이트하는 것은 상당히 높은 우선순위로 간주되어야 합니다.

    확장 모듈을 새 API로 업그레이드하려면 다음 단계를 수행하십시오:

  • Py_TPFLAGS_GCPy_TPFLAGS_HAVE_GC 로 변경하십시오.

  • 객체를 할당할 때는 PyObject_GC_New() 또는 PyObject_GC_NewVar() 를 사용하십시오.

    객체를 해제할 때는 PyObject_GC_Del() 을 사용하십시오.

  • PyObject_GC_Init()PyObject_GC_Track() 으로, PyObject_GC_Fini()PyObject_GC_UnTrack() 으로 변경하십시오.

  • 객체 크기 계산에서 PyGC_HEAD_SIZE 를 제거하십시오.

  • PyObject_AS_GC()PyObject_FROM_GC() 호출을 제거하십시오.

  • PyArg_ParseTuple() 에 새로운 et 포맷 시퀀스가 추가되었습니다. et 은 매개변수와 인코딩 이름을 모두 받으며, 매개변수가 유니코드 문자열인 경우 해당 인코딩으로 변환하고 8비트 문자열인 경우 이미 원하는 인코딩 상태라고 가정하고 그대로 둡니다. 이는 8비트 문자열이 파이썬의 기본 ASCII 인코딩에 있다고 가정하고 지정된 새 인코딩으로 변환하는 es 포맷 문자와는 다릅니다. (M.-A. Lemburg가 기여했으며, 다음 섹션에서 설명하는 Windows용 MBCS 지원을 위해 사용됩니다.)

  • 더 단순하고 아마도 더 빠른 다른 인자 파싱 함수인 PyArg_UnpackTuple() 이 추가되었습니다. 호출자는 포맷 문자열을 지정하는 대신, 예상되는 최소 및 최대 인수 개수와 인자 값으로 채워질 PyObject* 변수들에 대한 일련의 포인터를 제공합니다.

  • 인수가 없거나 단일 유형이 지정되지 않은 인수를 가진 메서드 구현을 단순화하기 위해 메서드 정의 테이블에 두 개의 새로운 플래그인 METH_NOARGSMETH_O 가 사용 가능해졌습니다. 이러한 메서드를 호출하는 것은 METH_VARARGS 를 사용하는 대응하는 메서드를 호출하는 것보다 더 효율적입니다. 또한, 오래된 METH_OLDARGS 스타일의 C 메서드 작성법은 이제 공식적으로 폐지되었습니다.

  • 비교적 최신인 snprintf()vsnprintf() C 라이브러리 API에 대한 교차 플랫폼 구현을 제공하기 위해 두 개의 새로운 래퍼 함수인 PyOS_snprintf()PyOS_vsnprintf() 가 추가되었습니다. 표준 sprintf()vsprintf() 함수와 달리, 파이썬 버전은 버퍼 오버런을 방지하기 위해 사용되는 버퍼의 범위를 확인합니다. (M.-A. Lemburg 기여.)

  • _PyTuple_Resize() 함수에서 사용되지 않는 매개변수가 하나 제거되어 이제 3개가 아닌 2개의 매개변수를 받습니다. 세 번째 인자는 결코 사용된 적이 없으므로, 이전 버전의 코드를 파이썬 2.2로 포팅할 때 간단히 생략할 수 있습니다.

기타 변경 사항 및 수정

평소와 마찬가지로 소스 트리 전반에 걸쳐 다양한 개선 사항과 버그 수정이 이루어졌습니다. CVS 변경 로그를 조사한 결과 파이썬 2.1과 2.2 사이에서 527개의 패치가 적용되고 683개의 버그가 수정되었으며, 2.2.1에서는 139개의 패치가 적용되고 143개의 버그가 수정되었고, 2.2.2에서는 106개의 패치가 적용되고 82개의 버그가 수정되었습니다. 이 수치는 실제보다 과소평가되었을 가능성이 높습니다.

주요 변경 사항 중 일부는 다음과 같습니다:

  • Jack Jansen이 유지 관리하는 파이썬의 macOS 포트 코드가 이제 메인 파이썬 CVS 트리에 포함되었으며, MacOS X를 지원하기 위한 많은 변경이 이루어졌습니다.

    가장 중요한 변화는 Python을 프레임워크로 구축할 수 있는 기능이며, 이는 Python 컴파일 시 구성 스크립트에 --enable-framework 옵션을 제공함으로써 가능해집니다. Jack Jansen에 따르면, “이 기능은 독립된 Python 설치본과 OS X 프레임워크 “glue”를 /Library/Frameworks/Python.framework (또는 사용자가 선택한 다른 위치)에 설치합니다. 현재로서는 이 기능으로 인해 즉각적인 추가 이점이 거의 없지만(실제로, Python을 찾기 위해 PATH를 변경해야 하는 단점도 있습니다), 이는 완전한 기능을 갖춘 Python 애플리케이션 제작, MacPython IDE 포팅, 그리고 가능성이 있는 표준 OSA 스크립팅 언어로서의 Python 활용 등 훨씬 더 많은 것을 위한 기반이 됩니다.”

    윈도우 생성, QuickTime, 스크립팅 등과 같은 MacOS API와 인터페이스하는 대부분의 MacPython 도구 모음이 OS X로 포팅되었으나, 현재 setup.py 에서 주석 처리되어 있습니다. 이 도구 모음들을 테스트하고 싶은 사용자는 수동으로 주석을 해제할 수 있습니다.

  • 키워드 인자를 받지 않는 내장 함수에 키워드 인자를 전달하면 이제 “function takes no keyword arguments”라는 메시지와 함께 TypeError 예외가 발생합니다.

  • Python 2.1에서 확장 모듈로 추가된 약한 참조(Weak references)는 이제 새로운 스타일의 클래스 구현에 사용되므로 핵심 기능의 일부가 되었습니다. 이에 따라 ReferenceError 예외가 weakref 모듈에서 내장 예외로 이동했습니다.

  • Tim Peters가 작성한 새로운 스크립트인 Tools/scripts/cleanfuture.py`는 Python 소스 코드에서 이상 사용되지 않는 ``__future__` 문을 자동으로 제거합니다.

  • 내장 함수 compile() 에 추가적인 flags 인자가 추가되어, IDLE 및 기타 개발 환경에서 제공되는 시뮬레이션된 셸에서도 __future__ 문의 동작을 올바르게 확인할 수 있습니다. 이는 PEP 264 에 설명되어 있습니다. (Michael Hudson이 기여함.)

  • Python 1.6과 함께 도입된 새로운 라이선스는 GPL과 호환되지 않았습니다. 이 문제는 2.2 버전의 라이선스에서 사소한 텍스트 수정을 통해 해결되었으며, 이제 다시 GPL 기반 프로그램 내에 Python을 포함하는 것이 법적으로 허용됩니다. 참고로 Python 자체는 GPL이 아니며, 이전과 마찬가지로 본질적으로 BSD 라이선스와 동일한 라이선스 하에 있습니다. 라이선스 변경 사항은 Python 2.0.1 및 2.1.1 출시 버전에도 적용되었습니다.

  • Windows에서 유니코드 파일명이 제공되면, 이제 Python은 이를 Microsoft 파일 API에서 사용하는 MBCS 인코딩 문자열로 변환합니다. MBCS가 파일 API에 명시적으로 사용되기 때문에, Python이 기본 인코딩으로 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에서 사라집니다. :class:()!xrange` 객체는 슬라이싱, 시퀀스 곱셈 및 in 연산자를 지원함으로써 전체 시퀀스 유형처럼 행동하려고 시도했지만, 이러한 기능들은 거의 사용되지 않았으며 버그가 많았습니다. 또한 tolist() 메서드와 start, stop, step 속성도 폐지됩니다. C 수준에서 PyRange_New() 함수의 네 번째 인자인 repeat 역시 폐지되었습니다.

  • 딕셔너리 구현에 대한 다수의 패치가 적용되었으며, 이는 주로 딕셔너리가 해시 값이 은밀하게 변경되거나 포함된 딕셔너리를 변형하는 객체를 포함할 때 발생할 수 있는 핵심 덤프를 수정하기 위한 것입니다. 한동안 python-dev 커뮤니티는 Michael Hudson이 코어 덤프 사례를 찾고, Tim Peters가 버그를 수정하고, 다시 Michael이 다른 사례를 찾는 과정이 반복되는 일정한 리듬을 유지했습니다.

  • Windows에서 여러 패치의 기여 덕분에 Python을 Borland C로 컴파일할 수 있게 되었으나, 아직 완전히 기능하는 것은 아닙니다. (하지만 이것도 진전입니다…)

  • 또 다른 Windows 개선 사항으로, Wise Solutions가 PythonLabs에 그들의 InstallerMaster 8.1 시스템을 아낌없이 제공해 주었습니다. 이전의 PythonLabs Windows 설치 프로그램은 구식이 된 Wise 5.0a를 사용했습니다. (Tim Peters가 패키징함.)

  • 이제 Windows에서 .pyw 로 끝나는 파일을 가져올(import) 수 있습니다. .pyw 는 Windows 전용 사양으로, 출력 내용을 표시하기 위해 DOS 콘솔이 뜨는 것을 방지하고자 PYTHON.EXE 대신 PYTHONW.EXE를 사용하여 스크립트를 실행해야 함을 나타냅니다. 이 패치는 이러한 스크립트가 모듈로도 사용될 수 있는 경우에 이를 가져올 수 있게 해줍니다. (David Bolen이 구현함.)

  • Python이 확장 모듈을 로드하기 위해 C dlopen() 함수를 사용하는 플랫폼에서, 이제 sys.getdlopenflags()sys.setdlopenflags() 함수를 사용하여 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.

분실물 보관소