Что нового в Python 2.2

Автор:А.М. Кухлинг

Вступление

В этой статье объясняются новые функции Python 2.2.2, выпущенного 14 октября 2002 г. Python 2.2.2 — это релиз Python 2.2 с исправлениями ошибок, изначально выпущенный 21 декабря 2001 г.

Python 2.2 можно рассматривать как «релиз очистки». Есть некоторые функции, такие как генераторы и итераторы, которые являются совершенно новыми, но большинство изменений, какими бы значительными и далеко идущими они ни были, направлены на устранение несоответствий и темных углов языковой конструкции.

В этой статье не делается попытка предоставить полную спецификацию новых функций, а вместо этого представлен удобный обзор. Для получения полной информации вы должны обратиться к документации для Python 2.2, такой как Справочник по библиотеке Python и Справочное руководство по Python. Если вы хотите понять полную реализацию и обоснование дизайна для изменения, обратитесь к PEP для конкретной новой функции.

PEP 252 и 253: Изменения типа и класса

Самые большие и далеко идущие изменения в Python 2.2 касаются модели объектов и классов Python. Изменения должны быть совместимы с предыдущими версиями, поэтому вполне вероятно, что ваш код продолжит работать без изменений, но данные изменения предоставляют некоторые удивительные новые возможности. Прежде чем приступить к этому, самому длинному и сложному разделу этой статьи, я сделаю обзор изменений и предложу несколько комментариев.

Давным-давно я написал веб-страницу с перечислением недостатков в дизайне Python. Одним из наиболее существенных недостатков было то, что невозможно создать подкласс типов Python, реализованных в C. В частности, невозможно создать подкласс встроенных типов, поэтому вы не можете просто подклассировать, скажем, списки, чтобы добавить один полезный метод. им. Модуль UserList предоставляет класс, который поддерживает все методы списков и может быть далее подклассом, но есть много кода C, который ожидает обычный список Python и не принимает экземпляр UserList.

В Python 2.2 это исправлено, а в процессе добавлено несколько интересных новых возможностей. Краткое изложение:

  • Вы можете создавать подклассы для встроенных типов, таких как списки и даже целые числа, и ваши подклассы должны работать везде, где требуется исходный тип.
  • Теперь можно определять статические методы и методы класса в дополнение к методам экземпляра, доступным в предыдущих версиях Python.
  • Также возможно автоматически вызывать методы при доступе к атрибуту экземпляра или его установке с помощью нового механизма под названием properties. Многие варианты использования __getattr__() можно переписать, чтобы вместо этого использовать свойства, что сделает результирующий код проще и быстрее. В качестве небольшого дополнительного преимущества атрибуты теперь также могут иметь строки документации.
  • Список допустимых атрибутов для экземпляра может быть ограничен определенным набором с помощью slots, что позволяет защититься от опечаток и, возможно, сделать возможными дополнительные оптимизации в будущих версиях Python.

Некоторые пользователи выразили обеспокоенность по поводу всех данных изменений. Конечно, говорят они, новые функции изящны и позволяют использовать всевозможные трюки, которые были невозможны в предыдущих версиях Python, но они также усложняют язык. Некоторые говорят, что они всегда рекомендовали Python из-за его простоты и считают, что его простота теряется.

Лично я считаю, что волноваться не о чем. Многие из новых функций довольно эзотеричны, и вы можете написать много кода на Python, даже не зная о них. Написать простой класс не сложнее, чем когда-либо, так что вам не нужно утруждать себя изучением или преподаванием их, если они действительно не нужны. Некоторые очень сложные задачи, которые раньше были возможны только на языке C, теперь можно выполнять и на чистом Python, и, на мой взгляд, это к лучшему.

Я не буду пытаться охватить каждый краеугольный случай и небольшие изменения, которые потребовались для того, чтобы новые функции заработали. Вместо этого в этом разделе будут нарисованы только широкие мазки. См. раздел Ссылки по теме, «Связанные ссылки», для получения дополнительной информации о новой объектной модели Python 2.2.

Старые и новые классы

Во-первых, вы должны знать, что Python 2.2 на самом деле имеет два типа классов: классические классы или классы старого стиля и классы нового стиля. Модель классов в старом стиле точно такая же, как модель классов в более ранних версиях Python. Все новые функции, описанные в этом разделе, относятся только к классам нового стиля. Это расхождение не должно длиться вечно; в конечном итоге классы старого стиля будут удалены, возможно, в Python 3.0.

Т. к. же определить класс нового стиля? Вы делаете это путём подкласса существующего класса нового стиля. Большинство встроенных типов Python, таких как целые числа, списки, словари и даже файлы, теперь являются классами нового стиля. Также был добавлен класс нового стиля с именем object, базовый класс для всех встроенных типов, поэтому, если встроенный тип не подходит, вы можете просто создать подкласс object:

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

Это означает, что операторы class, не имеющие базовых классов, всегда являются классическими классами в Python 2.2. (На самом деле вы также можете изменить это, установив переменную уровня модуля с именем __metaclass__ —, см. подробности в PEP 253 —, но проще просто создать подкласс object.)

Объекты типа для встроенных типов доступны как встроенные, названные с использованием хитрого трюка. Python всегда имел встроенные функции с именами int(), float() и str(). В версии 2.2 это уже не функции, а объекты типов, которые при вызове ведут себя как фабрики.

>>> int
<type 'int'>
>>> int('123')
123

Чтобы сделать множество типов полным, были добавлены новые объекты типов, такие как dict() и file(). Вот более интересный пример, добавление метода 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.

Дескрипторы

В предыдущих версиях Python не было единого способа узнать, какие атрибуты и методы поддерживаются объектом. Были некоторые неформальные соглашения, такие как определение атрибутов __members__ и __methods__, которые были списками имён, но часто автор типа расширения или класса не утруждал себя их определением. Вы могли бы вернуться к проверке __dict__ объекта, но когда использовалось наследование класса или произвольный хук __getattr__(), это все ещё могло быть неточным.

Одна большая идея, лежащая в основе новой модели классов, заключается в том, что API для описания атрибутов объекта с использованием descriptors был формализован. Дескрипторы определяют значение атрибута, указывая, является ли он методом или полем. С API дескриптора становятся возможными статические методы и методы класса, а также более экзотические конструкции.

Дескрипторы атрибутов — это объекты, которые живут внутри объектов класса и имеют несколько собственных атрибутов:

  • __name__ — это имя атрибута.
  • __doc__ — строка документации атрибута.
  • __get__(object) — это метод, извлекающий значение атрибута из object.
  • __set__(object, value) задаёт для атрибута object значение value.
  • __delete__(object, value) удаляет атрибут value object.

Например, когда вы пишете obj.x, Python фактически выполняет следующие шаги:

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() или что-то в этом роде), но такой синтаксис ещё не определён; это осталось для будущих версий Python.

Другие новые функции, такие как слоты и свойства, также реализованы в виде новых видов дескрипторов, и нетрудно написать класс дескриптора, который делает что-то новое. Например, можно было бы написать класс дескриптора, который позволял бы писать предусловия и постусловия в стиле 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(), не нужно ничего понимать в дескрипторах. Вот почему я думаю, что новые функции не увеличивают базовую сложность языка. Будет несколько волшебников, которым нужно знать об этом, чтобы написать eiffelmethod() или ZODB или что-то ещё, но большинство пользователей просто напишут код поверх полученных библиотек и проигнорируют детали реализации.

Множественное наследование: Алмазное правило

Множественное наследование также стало более полезным за счет изменения правил разрешения имён. Рассмотрим данный множество классов (диаграмма взята из PEP 253 Гвидо ван Россума):

      class A:
        ^ ^  def save(self): ...
       /   \
      /     \
     /       \
    /         \
class B     class C:
    ^         ^  def save(self): ...
     \       /
      \     /
       \   /
        \ /
      class D

Правило поиска для классических классов простое, но не очень умное; базовые классы просматриваются в глубину слева направо. Ссылка на D.save() будет искать классы D, B, а затем A, где save() будет найден и возвращён. C.save() вообще никогда не будет найден. Это плохо, потому что если метод C save() сохраняет какое-то внутреннее состояние, специфичное для C, его отсутствие приведёт к тому, что это состояние никогда не будет сохранено.

Классы нового стиля следуют другому алгоритму, который немного сложнее объяснить, но в данной ситуации он работает правильно. (Обратите внимание, что Python 2.3 изменил данный алгоритм на тот, который в большинстве случаев дает те же результаты, но дает более полезные результаты для действительно сложных графов наследования.)

  1. Перечислите все базовые классы, следуя классическому правилу поиска, и включает класс несколько раз, если он неоднократно посещался. В приведённом выше примере список посещенных классов — [D, B, A, C, A].
  2. Просканируйте список на наличие дубликатов классов. Если такие найдены, удалить все, кроме одного, оставив прошлый в списке. В приведённом выше примере список становится [D, B, C, A] после удаления дубликатов.

Следуя этому правилу, обращение к D.save() вернёт C.save(), что и является поведением, которое нам нужно. Это правило поиска такое же, как и в Common Lisp. Новая встроенная функция super() позволяет получить доступ к суперклассам класса без необходимости повторной реализации алгоритма Python. Наиболее часто используемая форма будет super(class, obj), которая возвращает связанный объект суперкласса (а не фактический объект класса). Эта форма будет использоваться в методах для вызова метода в суперклассе; например, метод save() D будет выглядеть так:

class D (B,C):
    def save (self):
        # Вызвать суперкласс .save()
        super(D, self).save()
        # Сохранить закрытую информацию D здесь
        ...

super() также может возвращать несвязанные объекты суперкласса при вызове как super(class) или super(class1, class2), но это, вероятно, не часто будет полезно.

Доступ к атрибутам

Большое количество сложных классов Python определяют перехватчики для доступа к атрибутам, используя __getattr__(); чаще всего это делается для удобства, чтобы сделать код более читабельным путём автоматического сопоставления доступа к атрибуту, такого как obj.parent, в вызов метода, такого как obj.get_parent. В Python 2.2 добавлено несколько новых способов управления доступом к атрибутам.

Во-первых, __getattr__(attr_name) по-прежнему поддерживается классами нового стиля, и ничего в нем не изменилось. Как и прежде, он будет вызываться при попытке доступа к obj.foo и отсутствии атрибута с именем foo в словаре экземпляра.

Классы нового стиля также поддерживают новый метод __getattribute__(attr_name). Разница между этими двумя методами заключается в том, что __getattribute__() вызывается как always всякий раз, когда осуществляется доступ к любому атрибуту, а старый __getattr__() вызывается только в том случае, если foo не найден в словаре экземпляра.

Однако поддержка свойств в Python 2.2 часто оказывается более простым способом перехвата ссылок на атрибуты. Написание метода __getattr__() сложно, потому что, чтобы избежать рекурсии, вы не можете использовать внутри них обычный доступ к атрибутам, а вместо этого должны возиться с содержимым __dict__. Методы __getattr__() также в конечном итоге вызываются Python, когда он проверяет другие методы, такие как __repr__() или __coerce__(), поэтому их следует писать с учётом этого. Наконец, вызов функции при каждом доступе к атрибуту приводит к значительной потере производительности.

property — это новый встроенный тип, который объединяет три функции, получающие, устанавливающие или удаляющие атрибут, и строку документации. Например, если вы хотите определить атрибут size, который вычисляется, но также может быть установлен, вы можете написать:

class C(object):
    def get_size (self):
        result = ... computation ...
        return result
    def set_size (self, size):
        ... вычислить что-то на основе размера
        и устанавливает внутреннее состояние соответствующим образом ...

    # Определение свойства. «Удалить данный атрибут»
    # метод определён как None, поэтому атрибут
    # нельзя удалить.
    size = property(get_size, set_size,
                    None,
                    "Storage size of this instance")

Это, безусловно, понятнее и проще в написании, чем пара методов __getattr__()/__setattr__(), которые проверяют наличие атрибута size и специально обрабатывают его, извлекая все остальные атрибуты из экземпляра __dict__. Доступы к size также являются единственными, которые должны выполнять работу по вызову функции, поэтому ссылки на другие атрибуты выполняются с их обычной скоростью.

Наконец, можно ограничить список атрибутов, на которые можно ссылаться в объекте, с помощью нового атрибута класса __slots__. Объекты Python обычно очень динамичны; в любое время можно определить новый атрибут экземпляра, просто выполнив 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'

Обратите внимание, как вы получаете AttributeError при попытке назначить атрибут, не указанный в __slots__.

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 на единицу каждый раз. Другими словами, наличие метода __getitem__() не означает, что использование file[5] для случайного доступа к шестому элементу будет работать, хотя на самом деле должно.

В Python 2.2 итерация может быть реализована отдельно, а методы __getitem__() могут быть ограничены классами, которые действительно поддерживают произвольный доступ. Основная идея итераторов проста. Для получения итератора используется новая встроенная функция iter(obj) или iter(C, sentinel). iter(obj) возвращает итератор для объекта obj, а iter(C, sentinel) возвращает итератор, который будет вызывать вызываемый объект C до тех пор, пока он не вернёт sentinel, чтобы сигнализировать о том, что итератор выполнен.

Классы Python могут определять метод __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 оператор Python for больше не ожидает последовательности; он ожидает что-то, для чего iter() вернёт итератор. Для обратной совместимости и удобства итератор автоматически создаётся для последовательностей, которые не реализуют __iter__() или слот tp_iter, поэтому for i in [1,2,3] по-прежнему будет работать. Везде, где интерпретатор Python зацикливается на последовательности, он был изменён для использования протокола итератора. Это означает, что вы можете делать такие вещи:

>>> L = [1,2,3]
>>> i = iter(L)
>>> a,b,c = i
>>> a,b,c
(1, 2, 3)

В некоторые базовые типы Python добавлена поддержка итераторов. Вызов 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 — Итераторы
Написано Ка-Пинг Йи и GvR; реализуется командой Python Labs, в основном компанией GvR и Тим Питерс.

PEP 255: Простые генераторы

Генераторы — ещё одна новая функция, которая взаимодействует с введением итераторов.

Вы, несомненно, знакомы с тем, как вызовы функций работают в Python или C. Когда вы вызываете функцию, она получает частное пространство имён, в котором создаются её локальные переменные. Когда функция достигает оператора return, локальные переменные уничтожаются, а результирующее значение возвращается вызывающей стороне. Последующий вызов той же функции получит новый множество локальных переменных. Но что, если бы локальные переменные не сбрасывались при выходе из функции? Что, если бы вы могли позже возобновить функцию с того места, где она остановилась? Это то, что обеспечивают генераторы; их можно рассматривать как возобновляемые функции.

Вот простейший пример функции-генератора:

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

Для генераторов было введено новое ключевой аргумент yield. Любая функция, содержащая оператор yield, является функцией-генератором; это обнаруживается компилятором байт-кода Python, который в результате специально компилирует функцию. Поскольку было введено новое ключевой аргумент, генераторы должны быть явно включены в модуле путём включения оператора from __future__ import generators в начало исходного кода модуля. В Python 2.3 данный оператор станет ненужным.

Когда вы вызываете функцию-генератор, она не возвращает ни одного значения; вместо этого он возвращает объект генератора, который поддерживает протокол итератора. При выполнении инструкции yield генератор выводит значение i, аналогичное инструкции return. Большая разница между операторами yield и return заключается в том, что при достижении yield состояние выполнения генератора приостанавливается, а локальные переменные сохраняются. При следующем вызове метода генератора next() функция возобновит выполнение сразу после оператора yield. (По сложным причинам оператор yield не допускается внутри блока try оператора tryfinally; прочитать PEP 255 для полного объяснения взаимодействия между yield и исключениями.)

Вот пример использования генератора 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 со значением, например return 5, является синтаксической ошибкой внутри функции-генератора. Окончание результатов генератора также можно обозначить, вызвав StopIteration вручную, или просто позволив потоку выполнения упасть с нижней части функции.

Вы можете добиться эффекта генераторов вручную, написав свой собственный класс и сохранив все локальные переменные генератора как переменные экземпляра. Например, возвращает список целых чисел можно, установив для self.count значение 0, а метод next() увеличит self.count и вернёт его. Однако для умеренно сложного генератора написать соответствующий класс было бы гораздо сложнее. Lib/test/test_generators.py содержит ряд более интересных примеров. Самый простой из них реализует обход дерева по порядку с использованием генераторов рекурсивно.

# Рекурсивный генератор, который генерирует листья дерева по порядку.
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-ферзей (размещение $N$ ферзей на шахматной доске размером $NxN$, чтобы ни одна ферзь не угрожал другому) и маршрута коня (маршрут, который ведёт коня на каждую клетку $ Шахматная доска NxN$ без посещения любой клетки дважды).

Идея генераторов пришла из других языков программирования, особенно из Icon (https://www.cs.arizona.edu/icon/), где идея генераторов является центральной. В Icon каждое выражение и вызов функции ведут себя как генератор. Один пример из «Обзора языка программирования Icon» на https://www.cs.arizona.edu/icon/docs/ipd266.htm дает представление о том, как это выглядит:

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

В Icon функция find() возвращает индексы, в которых встречается подстрока «или»: 3, 23, 33. В операторе if i сначала присваивается значение 3, но 3 меньше 5, поэтому сравнение не выполняется, и Icon повторяет попытку со вторым значением 23. 23 больше 5, поэтому теперь сравнение завершается успешно, и код выводит значение 23 на экран.

Python не заходит так далеко, как Icon, в принятии генераторов в качестве центральной концепции. Генераторы считаются новой частью основного языка Python, но их изучение или использование не являются обязательными; если они не решают какие-либо проблемы, которые у вас есть, не стесняйтесь их игнорировать. Одной из новых особенностей интерфейса Python по сравнению с интерфейсом Icon является то, что состояние генератора представлено в виде конкретного объекта (итератора), который можно передать другим функциям или сохранить в структуре данных.

См.также

PEP 255 — Простые генераторы
Сценарий: Нил Шеменауэр, Тим Питерс, Магнус Ли Хетланд. Реализовано в основном Нила Шеменауэра и Тима Питерса, с другими исправлениями от команды Python Labs.

PEP 237: Объединение длинных целых и целых чисел

В последних версиях стало раздражать различие между обычными целыми числами, которые на большинстве машин являются 32-битными значениями, и длинными целыми числами, которые могут иметь произвольный размер. Например, на платформах, поддерживающих файлы размером более 2**32 байт, метод файловых объектов tell() должен возвращать длинное целое число. Однако были различные части Python, которые ожидали простые целые числа и вызывали ошибку, если вместо этого предоставлялось длинное целое число. Например, в Python 1.5 в качестве индекса среза можно было использовать только обычные целые числа, а 'abc'[1L:] вызовет исключение TypeError с сообщением «индекс среза должен быть int».

Python 2.2 будет сдвигать значения с коротких на длинные целые по мере необходимости. Суффикс «L» больше не нужен для обозначения длинного целочисленного литерала, т. к. теперь компилятор сам выберет соответствующий тип. (Использование суффикса «L» не рекомендуется в будущих версиях Python 2.x, вызывая предупреждение в Python 2.4 и, вероятно, исключенное в Python 3.0.) Многие операции, которые раньше поднимали OverflowError, теперь будут возвращать длинное целое число в качестве их результат. Например:

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

В большинстве случаев целые и длинные целые теперь будут обрабатываться одинаково. Вы все ещё можете отличить их с помощью встроенной функции type(), но это редко требуется.

См.также

PEP 237 — Объединение длинных целых чисел и целых чисел
Авторы Моше Задка и Гвидо ван Россум. Реализовано в основном Гвидо ван Россум.

PEP 238: Смена оператора деления

Самое спорное изменение в Python 2.2 знаменует собой начало усилий по исправлению старой конструктивной ошибки, которая была в Python с самого начала. В настоящее время оператор деления Python, /, ведёт себя как оператор деления C, когда представлен двумя целочисленными аргументами: он возвращает целочисленный результат, который усекается, когда должна быть дробная часть. Например, 3/2 равно 1, а не 1,5, а (-1)/2 равно -1, а не -0,5. Это означает, что результаты деления могут неожиданно различаться в зависимости от типа двух операндов, а поскольку Python имеет динамическую типизацию, может быть сложно определить возможные типы операндов.

(Спор идет о том, является ли это в самом деле недостатком дизайна, и стоит ли ломать существующий код, чтобы это исправить. Это вызвало бесконечные дискуссии на python-dev, а в июле 2001 года вылилось в бурю язвительно-саркастических сообщений на comp.lang.python. Я выиграл не спорьте ни за одну из сторон здесь и буду придерживаться описания того, что реализовано в 2.2. Прочитать PEP 238 для сводки аргументов и контраргументов.)

Поскольку это изменение может привести к поломке кода, оно вводится очень постепенно. Python 2.2 начинает переход, но переход не будет завершён до Python 3.0.

Во-первых, я позаимствую терминологию из PEP 238. «Истинное деление» — это деление, знакомое большинству непрограммистов: 3/2 — это 1,5, 1/4 — это 0,25 и так далее. «Деление этажей» — это то, что в настоящее время делает оператор Python /, когда ему заданы целые операнды; результатом является пол значения, возвращаемого истинным делением. «Классическое деление» — текущее смешанное поведение /; он возвращает результат деления на пол, когда операнды являются целыми числами, и возвращает результат истинного деления, когда один из операндов является числом с плавающей запятой.

Вот изменения, которые вводит 2.2:

  • Новый оператор, //, является оператором подразделения этажей. (Да, мы знаем, что это похоже на символ комментария C++.) // always выполняет деление на пол вне зависимости от типа операндов, поэтому 1 // 2 равно 0, а 1.0 // 2.0 также равно 0,0.

    // всегда доступен в Python 2.2; вам не нужно включать его с помощью инструкции __future__.

  • При включении from __future__ import division в модуль оператор / будет изменён, чтобы возвращать результат истинного деления, поэтому 1/2 равно 0,5. Без оператора __future__ / по-прежнему означает классическое деление. Значение / по умолчанию не изменится до версии Python 3.0.

  • Классы могут определять методы с именами __truediv__() и __floordiv__() для перегрузки двух операторов деления. На уровне C в структуре PyNumberMethods также есть слоты, поэтому типы расширений могут определять два оператора.

  • Python 2.2 поддерживает некоторые аргументы командной строки для проверки того, будет ли код работать с измененной семантикой деления. Запуск python с -Q warn вызовет предупреждение всякий раз, когда деление применяется к двум целым числам. Вы можете использовать это, чтобы найти код, затронутый изменением, и исправить его. По умолчанию Python 2.2 просто выполняет классическое деление без предупреждения; предупреждение будет включено по умолчанию в Python 2.3.

См.также

PEP 238 — Смена оператора деления
Авторы Моше Задка и Гвидо ван Россум. Реализован Гвидо ван Россумом.

Изменения Юникода

Поддержка Юникод в Python была немного улучшена в версии 2.2. Строки Юникод обычно хранятся как UCS-2, как 16-битные целые числа без знака. Python 2.2 также можно скомпилировать для использования UCS-4, 32-битных целых чисел без знака, в качестве внутренней кодировки, указав --enable-unicode=ucs4 в скрипте configure. (Также можно указать --disable-unicode, чтобы полностью отключить поддержку Юникод.)

При построении для использования UCS-4 («широкий Python») интерпретатор может изначально обрабатывать символы Юникод от U+000000 до U+110000, поэтому диапазон допустимых значений для функции unichr() соответственно расширен. При использовании интерпретатора, скомпилированного для использования UCS-2 («узкий Python»), значения, превышающие 65535, по-прежнему будут вызывать исключение unichr() unichr(). Все это приведено в PEP 261, «Поддержка «широких» символов Юникод»; проконсультируйтесь с ним для получения дополнительной информации.

Другое изменение объяснить проще. С момента своего появления строки Юникод поддерживают метод encode() для преобразования строки в выбранную кодировку, такую как UTF-8 или Latin-1. Симметричный метод decode([*encoding*]) был добавлен к 8-битным строкам (но не к строкам Юникод) в версии 2.2. 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__(), аналогичный __str__().

encode(), decode() и __unicode__() были реализованы Марком- Андре Лембургом. Изменения для внутренней поддержки использования UCS-4 были реализованы Фредриком Лундом и Мартином фон Лёвисом.

См.также

PEP 261 — поддержка «широких» символов Юникод
Автор Пол Прескод.

PEP 227: Вложенные области

В Python 2.1 статически вложенные области были добавлены в качестве дополнительной функции, которая должна быть включена директивой from __future__ import nested_scopes. В версии 2.2 вложенные области больше не нужно специально включать, и теперь они всегда присутствуют. Остальная часть этого раздела является копией описания вложенных областей видимости из моего документа «Что нового в 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() всегда будет вызывать исключение NameError, поскольку привязка имени g не находится ни в её локальном пространстве имён, ни в пространстве имён уровня модуля. На практике это не представляет большой проблемы (как часто вы рекурсивно определяете внутренние функции подобным образом?), но это также делало использование выражения lambda более неуклюжим, и на практике это было проблемой. В коде, который использует lambda, вы часто можете обнаружить, что локальные переменные копируются, передавая их в качестве значений аргументов по умолчанию.

def find(self, name):
    "Возвращает список любых записей, равных 'name'"
    L = filter(lambda x, name=name: x == name,
               self.list_attribute)
    return L

В результате сильно страдает читабельность кода Python, написанного в строго функциональном стиле.

Наиболее значительным изменением в Python 2.2 является то, что для решения этой проблемы в язык была добавлена статическая область видимости. Во-первых, в приведённом выше примере аргумент по умолчанию name=name теперь не нужен. Проще говоря, когда заданному имени переменной не присваивается значение внутри функции (присваиванием или операторами def, class или import), ссылки на переменную будут искаться в локальном пространстве имён объемлющей области. Более подробное объяснение правил и разбор реализации можно найти в PEP.

Это изменение может вызвать некоторые проблемы совместимости для кода, в котором одно и то же имя переменной используется как на уровне модуля, так и в качестве локальной переменной внутри функции, содержащей дополнительные определения функций. Это кажется довольно маловероятным, поскольку такой код изначально был бы довольно запутанным для чтения.

Одним из побочных эффектов изменения является то, что операторы from module import * и exec стали недопустимыми внутри области действия функции при определенных условиях. В справочном руководстве по Python все время говорилось, что from module import * допустим только на верхнем уровне модуля, но интерпретатор CPython никогда раньше не применял это. В рамках реализации вложенных областей компилятор, который превращает исходный код Python в байт- коды, должен генерировать другой код для доступа к переменным в содержащей области. from module import * и exec не позволяют компилятору понять это, потому что они добавляют имена в локальное пространство имён, которые неизвестны во время компиляции. Поэтому, если функция содержит определения функций или выражения lambda со свободными переменными, компилятор отметит это, вызвав исключение SyntaxError.

Чтобы сделать предыдущее объяснение немного яснее, вот пример:

x = 1
def f():
    # В следующей строке синтаксическая ошибка
    exec 'x=2'
    def g():
        return x

Строка 4, содержащая оператор exec, является синтаксической ошибкой, поскольку exec определяет новую локальную переменную с именем x, к значению которой должен обращаться g().

Это не должно быть большим ограничением, т. к. exec редко используется в большей части кода Python (и когда он используется, это в любом случае часто является признаком плохого дизайна).

См.также

PEP 227 — статически вложенные области
Написано и реализовано Джереми Хилтоном.

Новые и улучшенные модули

  • Модуль xmlrpclib был добавлен в стандартную библиотеку Фредриком Лундом и обеспечивает поддержку написания клиентов XML-RPC. XML-RPC — это простой протокол удаленного вызова процедур, построенный на основе HTTP и XML. Например, следующий фрагмент извлекает список каналов RSS из сети O’Reilly, а затем перечисляет последние заголовки для одного канала:

    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. См. http://xmlrpc.scripting.com/ для получения дополнительной информации о XML-RPC.

  • Новый модуль hmac реализует алгоритм HMAC, описанный в RFC 2104. (Предоставлено Герхардом Херингом)

  • Несколько функций, которые первоначально возвращали длинные кортежи, теперь возвращают псевдопоследовательности, которые по-прежнему ведут себя как кортежи, но также имеют мнемонические атрибуты, такие как memberst_mtime или tm_year. Расширенные функции включают stat(), fstat(), statvfs() и fstatvfs() в модуле os, а также localtime(), gmtime() и strptime() в модуле time.

    Например, чтобы получить размер файла, используя старые кортежи, вы в конечном итоге написали что-то вроде file_size = os.stat(filename)[stat.ST_SIZE], но теперь это можно записать более четко как file_size = os.stat(filename).st_size.

    Оригинальный патч для этой функции был предоставлен Ником Мэтьюсоном.

  • Профилировщик Python был значительно переработан, и были исправлены различные ошибки в его выводе. (Предоставлено Фредом Л. Дрейком-младшим и Тимом Питерсом)

  • Модуль socket может быть скомпилирован для поддержки IPv6; указать параметр --enable-ipv6 в сценарии настройки Python. (Предоставлено Дзюн-ичиро «itojun» Хагино)

  • Два новых символа формата были добавлены в модуль struct для 64-битных целых чисел на платформах, поддерживающих тип C long long. q — для 64-битного целого числа со знаком, а Q — для беззнакового. Значение возвращается в длинном целочисленном типе Python. (Предоставлено Тимом Питерсом)

  • В интерактивном режиме интерпретатора есть новая встроенная функция help(), которая использует модуль pydoc, представленный в Python 2.1, для предоставления интерактивной справки. help(object) отображает любой доступный текст справки о object. help() без аргументов помещает вас в интерактивную справочную утилиту, где вы можете ввести имена функций, классов или модулей, чтобы прочитать их текст справки. (Предоставлено Гвидо ван Россумом с использованием модуля Ка-Пинг Йи pydoc)

  • В механизм SRE, лежащий в основе модуля re, внесены различные исправления и улучшения производительности. Например, функции re.sub() и re.split() были переписаны на языке C. Другой внесенный патч ускоряет определённые диапазоны символов Юникод в два раза, а также новый метод finditer(), который возвращает итератор по всем непересекающимся совпадениям в заданной строке. (SRE поддерживается Фредриком Лундом. Патч BIGCHARSET был предоставлен Мартином фон Лёвисом)

  • Модуль smtplib теперь поддерживает RFC 2487, «Безопасный SMTP через TLS», поэтому теперь можно шифровать SMTP-трафик между программой Python и агентом почтового транспорта, которому передаётся сообщение. smtplib также поддерживает аутентификацию SMTP. (Предоставлено Герхардом Херингом)

  • Модуль imaplib, поддерживаемый Пирсом Лаудером, поддерживает несколько новых расширений: расширение NAMESPACE, определенное в RFC 2342, SORT, GETACL и SETACL. (Предоставлено Энтони Бакстером и Мишелем Пеллетье)

  • Анализ адресов электронной почты модулем rfc822 теперь соответствует RFC 2822, обновлению RFC 822. (Имя модуля не будет изменено на rfc2822.) Также был добавлен новый пакет email для разбора и создания сообщений электронной почты. (Предоставлено Барри Варшавой и является результатом его работы над Mailman.)

  • Модуль difflib теперь содержит новый класс Differ для создания удобочитаемых списков изменений («дельта») между двумя последовательностями строк текста. Есть также две функции генератора, ndiff() и restore(), которые соответственно возвращают дельту из двух последовательностей или одну из исходных последовательностей из дельты. (Грязная работа, внесенная Дэвидом Гуджером, из кода ndiff.py Тима Питерса, который затем выполнил генераторизацию.)

  • В модуль string добавлены новые константы ascii_letters, ascii_lowercase и ascii_uppercase. В стандартной библиотеке было несколько модулей, которые использовали string.letters для обозначения диапазонов от A до Z, но это предположение неверно, когда используются локали, потому что string.letters меняется в зависимости от набора допустимых символов, определенных текущей локалью. Все модули с ошибками были исправлены для использования ascii_letters вместо этого. (Сообщено неизвестным лицом; исправлено Фредом Л. Дрейком- младшим)

  • Модуль mimetypes теперь упрощает использование альтернативных баз данных типа MIME за счёт добавления класса MimeTypes, который принимает список имён файлов для анализа. (Предоставлено Фредом Л. Дрейком- младшим)

  • В модуль threading был добавлен класс Timer, который позволяет планировать выполнение действия в будущем. (Предоставлено Итамаром Штулл-Траурингом)

Изменения и исправления интерпретатора

Некоторые изменения касаются только тех, кто имеет дело с интерпретатором Python на уровне C, потому что они пишут модули расширения Python, встраивают интерпретатор или просто модифицируют сам интерпретатор. Если вы пишете только код на Python, ни одно из описанных здесь изменений не сильно на вас повлияет.

  • Функции профилирования и трассировки теперь могут быть реализованы на C, который может работать на гораздо более высоких скоростях, чем функции на основе Python, и должен снизить накладные расходы на профилирование и трассировку. Это будет интересно авторам сред разработки для Python. В API Python были добавлены две новые функции C: PyEval_SetProfile() и PyEval_SetTrace(). Существующие функции sys.setprofile() и sys.settrace() все ещё существуют, и они были просто изменены для использования нового интерфейса уровня C. (Предоставлено Фредом Л. Дрейком- младшим)

  • Добавлен ещё один низкоуровневый API, представляющий интерес в первую очередь для разработчиков отладчиков и средств разработки Python. PyInterpreterState_Head() и PyInterpreterState_Next() позволяют вызывающему объекту просматривать все существующие объекты интерпретатора; PyInterpreterState_ThreadHead() и PyThreadState_Next() позволяют зацикливаться на всех состояниях потока для данного интерпретатора. (Предоставлено Дэвидом Бизли.)

  • Интерфейс уровня C для сборщика мусора был изменён, чтобы упростить написание типов расширений, поддерживающих сборку мусора, и отладку неправильного использования функций. Различные функции имеют немного разную семантику, поэтому кучу функций пришлось переименовать. Расширения, использующие старый API, по-прежнему будут компилироваться, но не будут участвовать в сборке мусора, поэтому их обновление для версии 2.2 следует рассматривать как довольно высокий приоритет.

    Чтобы обновить модуль расширения до нового API, выполнить следующие действия:

  • Переименуйте Py_TPFLAGS_GC() в PyTPFLAGS_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-битная строка, предполагая, что она уже находится в нужной кодировке. . Это отличается от символа формата es, который предполагает, что 8-битные строки находятся в кодировке Python по умолчанию ASCII, и преобразует их в указанную новую кодировку. (Предоставлено М.-А. Лембургом и используется для поддержки MBCS в Windows, описанной в следующем разделе.)

  • Добавлена другая функция разбора аргументов, PyArg_UnpackTuple(), которая проще и предположительно быстрее. Вместо указания строки формата вызывающая сторона просто указывает минимальное и максимальное количество ожидаемых аргументов и множество указателей на переменные PyObject*, которые будут заполнены значениями аргументов.

  • Два новых флага METH_NOARGS и METH_O доступны в таблицах определения методов для упрощения реализации методов без аргументов или с одним нетипизированным аргументом. Вызов таких методов более эффективен, чем вызов соответствующего метода, использующего METH_VARARGS. Кроме того, старый стиль написания методов C METH_OLDARGS теперь официально объявлен устаревшим.

  • Две новые функции-оболочки, PyOS_snprintf() и PyOS_vsnprintf(), были добавлены для обеспечения межплатформенных реализаций для относительно новых API C lib snprintf() и vsnprintf(). В отличие от стандартных функций sprintf() и vsprintf() версии Python проверяют границы буфера, используемые для защиты от переполнения буфера. (Предоставил М.-А. Лембург.)

  • Функция _PyTuple_Resize() потеряла неиспользуемый параметр, поэтому теперь она принимает 2 параметра вместо 3. Третий аргумент никогда не использовался, и его можно просто отбросить при переносе кода из более ранних версий на Python 2.2.

Другие изменения и исправления

Как обычно, по всему дереву исходников было разбросано множество других улучшений и исправлений. Поиск в журналах изменений CVS показал, что между Python 2.1 и 2.2 было применено 527 патчей и исправлено 683 ошибки; 2.2.1 применено 139 патчей и исправлено 143 ошибки; 2.2.2 применено 106 патчей и исправлено 82 ошибки. Данные цифры, скорее всего, занижены.

Некоторые из наиболее заметных изменений:

  • Код для порта MacOS для Python, поддерживаемый Джеком Янсеном, теперь хранится в основном дереве Python CVS, и для поддержки MacOS X было внесено много изменений.

    Наиболее значительным изменением является возможность сборки Python в качестве фреймворка, что достигается путём предоставления параметра --enable-framework в скрипт configure при компиляции Python. По словам Джека Янсена, «это устанавливает автономную установку Python плюс «клей» инфраструктуры OS X в /Library/Frameworks/Python.framework (или другое место по выбору). На данный момент от этого мало дополнительных преимуществ (на самом деле, есть недостаток, что вы должны изменить свой PATH, чтобы иметь возможность найти Python), но это основа для создания полноценного приложения Python, переноса MacPython IDE, возможного использования Python в качестве стандартного языка сценариев OSA и многого другого»

    Большинство модулей набора инструментов MacPython, которые взаимодействуют с API-интерфейсами MacOS, такими как работа с окнами, QuickTime, сценарии и т. д., были перенесены в OS X, но они остались закомментированными в setup.py. Те, кто хочет поэкспериментировать с этими модулями, могут раскомментировать их вручную.

  • Ключевые аргументы, переданные встроенным функциям, которые их не принимают, теперь вызывают исключение TypeError с сообщением «function не принимает ключевые аргументы».

  • Слабые ссылки, добавленные в Python 2.1 в качестве модуля расширения, теперь являются частью ядра, поскольку они используются при реализации классов нового стиля. Таким образом, исключение ReferenceError переместилось из модуля weakref во встроенное исключение.

  • Новый скрипт Tools/scripts/cleanfuture.py Тима Питерса автоматически удаляет устаревшие операторы __future__ из исходного кода Python.

  • Во встроенную функцию compile() добавлен дополнительный аргумент flags, поэтому поведение операторов __future__ теперь можно правильно наблюдать в смоделированных оболочках, таких как те, что представлены IDLE и другими средами разработки. Это приведено в PEP 264. (Предоставлено Майклом Хадсоном.)

  • Новая лицензия, представленная в Python 1.6, не была совместима с GPL. Это исправлено некоторыми незначительными текстовыми изменениями в лицензии 2.2, поэтому теперь можно снова встраивать Python в программу под GPL. Обратите внимание, что сам Python не находится под лицензией GPL, а вместо этого находится под лицензией, которая по сути эквивалентна лицензии BSD, как это было всегда. Изменения лицензии также были применены к выпускам Python 2.0.1 и 2.1.1.

  • При представлении Юникод имени файла в Windows Python теперь преобразует его в строку в кодировке MBCS, как это используется файловыми API Microsoft. Поскольку MBCS явно используется файловыми API, выбор Python ASCII в качестве кодировки по умолчанию вызывает раздражение. В Unix используется множество символов локали, если доступен locale.nl_langinfo(CODESET). (Поддержка Windows была предоставлена Марком Хаммондом при содействии Марка-Андре Лембурга. Поддержка Unix была добавлена Мартином фон Лёвисом)

  • Поддержка больших файлов теперь включена в Windows. (Предоставлено Тимом Питерсом)

  • Сценарий Tools/scripts/ftpmirror.py теперь анализирует файл .netrc, если он у вас есть. (Предоставлено Майком Ромбергом.)

  • Некоторые функции объекта, возвращаемые функцией xrange(), теперь устарели и вызывают предупреждения при доступе к ним; они исчезнут в Python 2.3. Объекты xrange пытались притвориться, что они являются полными типами последовательностей, поддерживая срезы, умножение последовательностей и оператор in, но данные функции использовались редко и поэтому содержали ошибки. Метод tolist() и атрибуты start, stop и step также устарели. На уровне C четвертый аргумент функции PyRange_New(), repeat, также объявлен устаревшим.

  • В реализацию словаря было внесено множество исправлений, в основном для исправления потенциальных дампов ядра, если словарь содержит объекты, которые тайком изменили свое хеш-значение или мутировали словарь, в котором они содержались. На какое-то время python-dev впал в мягкий ритм Майкл Хадсон нашёл дело, из-за которого сбрасывалось ядро, Тим Питерс исправил ошибку, Майкл нашёл ещё одно дело, и так по кругу.

  • В Windows Python теперь можно скомпилировать с помощью Borland C благодаря ряду исправлений, внесенных Стивеном Хансеном, хотя результат ещё не полностью функционален. (Но это прогресс будет…)

  • Ещё одно усовершенствование Windows: компания Wise Solutions щедро предложила PythonLabs использовать свою систему InstallerMaster 8.1. Ранее установщики PythonLabs для Windows использовали Wise 5.0a, который начал показывать свой возраст. (Упаковано Тимом Питерсом.)

  • Файлы, оканчивающиеся на .pyw, теперь можно импортировать в Windows. .pyw — это только для Windows, используется для указания того, что сценарий необходимо запустить с использованием PYTHONW.EXE вместо PYTHON.EXE, чтобы предотвратить появление консоли DOS для отображения вывода. Данный патч позволяет импортировать такие сценарии, если их также можно использовать в качестве модулей. (Реализовано Дэвидом Боленом)

  • На платформах, где Python использует функцию C dlopen() для загрузки модулей расширения, теперь можно установить флаги, используемые dlopen(), с помощью функций sys.getdlopenflags() и sys.setdlopenflags(). (Предоставлено Брэмом Столком.)

  • Встроенная функция pow() больше не поддерживает 3 аргумента при передаче чисел с плавающей запятой. pow(x, y, z) возвращает (x**y) % z, но это никогда не бывает полезно для чисел с плавающей запятой, и окончательный результат непредсказуемо зависит от платформы. Такой вызов, как pow(2.0, 8.0, 7.0), теперь вызовет исключение TypeError.

Благодарности

Автор хотел бы поблагодарить следующих людей за предложения, исправления и помощь с различными черновиками этой статьи: Фред Бреммер, Кит Бриггс, Эндрю Далк, Фред Л. Дрейк-младший, Карел Феллингер, Дэвид Гуджер, Марк Хаммонд, Стивен Хансен, Майкл Хадсон, Джек Янсен, Марк-Андре Лембург, Мартин фон Лёвис, Фредрик Лунд, Майкл МакЛэй, Ник Мэтьюсон, Пол Мур, Густаво Нимейер, Дон О«Доннелл, Йоонас Пааласма, Тим Питерс, Йенс Куэйд, Том Рейнхардт, Нил Шеменауэр, Гвидо ван Россум, Грег Уорд, Эдвард Уэлборн.