Что нового в 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 Library Reference и Python Reference Manual. Если вы хотите понять полную реализацию и обоснование изменений, обратитесь к PEP для конкретной новой функции.

PEPs 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 содержал класс, который эмулировал все методы объекта file, а также добавлял метод lock(), но этот класс нельзя было передавать внутренним функциям, которые ожидали встроенный файл, что стало возможным благодаря нашему новому LockableFile.

Дескрипторы

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

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

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

  • __name__ - это имя атрибута.

  • __doc__ - документальная строка атрибута.

  • __get__(object) - это метод, который извлекает значение атрибута из объекта.

  • __set__(object, value) устанавливает атрибут объекта в значение.

  • __delete__(object, value) удаляет атрибут значение из объекта.

Например, когда вы пишете 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):
        # The actual function
        ...
    def pre_f(self):
        # Check preconditions
        ...
    def post_f(self):
        # Check postconditions
        ...

    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), которая возвращает связанный объект суперкласса (а не сам объект класса). Эта форма используется в методах для вызова метода в суперклассе; например, метод D save() save() будет выглядеть так:

class D (B,C):
    def save (self):
        # Call superclass .save()
        super(D, self).save()
        # Save D's private information here
        ...

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__() всегда вызывается при обращении к любому атрибуту, в то время как старый __getattr__() вызывается только в том случае, если foo не найден в словаре экземпляра.

Однако поддержка properties в 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):
        ... compute something based on the size
        and set internal state appropriately ...

    # Define a property.  The 'delete this attribute'
    # method is defined as None, so the attribute
    # can't be deleted.
    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'

Обратите внимание, что при попытке присвоить атрибут, не указанный в __slots__, вы получаете 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, увеличивающимся на единицу каждый раз. Другими словами, наличие метода __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 оператор for в Python больше не ожидает последовательности; он ожидает чего-то, для чего 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:
    # do something for each line
    ...

Обратите внимание, что в итераторе можно двигаться только вперед; нет способа получить предыдущий элемент, сбросить итератор или создать его копию. Объект итератора мог бы предоставить такие дополнительные возможности, но протокол итератора требует только метода next().

См.также

PEP 234 - Итераторы

Написано Ka-Ping Yee и 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 содержит ряд более интересных примеров. Самый простой из них реализует последовательный обход дерева с рекурсивным использованием генераторов.

# A recursive generator that generates Tree leaves in 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-Queens (размещение $N$ ферзей на шахматной доске $NxN$ так, чтобы ни один ферзь не угрожал другому) и Knight’s Tour (маршрут, по которому конь попадает на каждую клетку шахматной доски $NxN$, не посещая ни одну клетку дважды).

Идея генераторов пришла из других языков программирования, особенно из Icon (https://www2.cs.arizona.edu/icon/), где идея генераторов занимает центральное место. В Icon каждое выражение и вызов функции ведут себя как генератор. Один пример из «Обзора языка программирования Icon» на https://www2.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 с сообщением „slice index must be 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++.) // всегда выполняет деление на пол, независимо от типа операндов. всегда выполняет деление на пол, независимо от типов его операндов, поэтому 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 - Изменение оператора деления

Авторы: Моше Задка и Гвидо ван Россум. Реализовано Гвидо ван Россумом.

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

В версии 2.2 поддержка Юникода в Python была немного расширена. Строки Юникода обычно хранятся в 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() вызовет исключение ValueError. Все это описано в PEP 261, «Поддержка „широких“ символов Юникода»; обратитесь к нему за более подробной информацией.

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

С помощью этой новой возможности были добавлены кодеки для задач, не связанных напрямую с Юникодом. Например, были добавлены кодеки для uu-encoding, кодировки 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):
    "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 заключается в том, что для решения этой проблемы в язык был добавлен статический скопинг. В качестве первого эффекта, аргумент по умолчанию 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():
    # The next line is a syntax error
    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 Network, а затем выводит список последних заголовков для одного канала:

    import xmlrpclib
    s = xmlrpclib.Server(
          'http://www.oreillynet.com/meerkat/xml-rpc/server.php')
    channels = s.meerkat.getChannels()
    # channels is a list of dictionaries, like this:
    # [{'id': 4, 'title': 'Freshmeat Daily News'}
    #  {'id': 190, 'title': '32Bits Online'},
    #  {'id': 4549, 'title': '3DGamers'}, ... ]
    
    # Get the items for one channel
    items = s.meerkat.getItems( {'channel': 4} )
    
    # 'items' is another list of dictionaries, like this:
    # [{'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 реализует алгоритм 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 в сценарии configure Python. (Внесено Дзюн-итиро «itojun» Хагино).

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

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

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

  • Модуль smtplib теперь поддерживает RFC 2487, «Secure SMTP over 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-Za-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 в Py_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 принимает параметр и имя кодировки и преобразует параметр в заданную кодировку, если параметр оказывается строкой Unicode, или оставляет его в покое, если это 8-битная строка, предполагая, что она уже находится в нужной кодировке. Это отличается от символа формата es, который предполагает, что 8-битные строки находятся в кодировке ASCII, принятой в Python по умолчанию, и преобразует их в указанную новую кодировку. (Предоставлено M.-A. Лембургом и используется для поддержки MBCS в Windows, описанной в следующем разделе).

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

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

  • Две новые функции-обертки, PyOS_snprintf() и PyOS_vsnprintf(), были добавлены для обеспечения кроссплатформенной реализации относительно новых snprintf() и vsnprintf() C lib API. В отличие от стандартных функций 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 ошибки. Эти цифры, скорее всего, являются заниженными.

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

  • Код порта Python для MacOS, поддерживаемый Джеком Янсеном, теперь хранится в основном CVS-дереве Python, и в него было внесено множество изменений для поддержки 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 с сообщением «функция не принимает аргументов ключевых слов».

  • Слабые ссылки, добавленные в 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.

  • При представлении имени файла в Unicode под 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.

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

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