Что нового в Python 3.1

Автор:

Раймонд Хеттингер

В этой статье рассказывается о новых возможностях Python 3.1 по сравнению с 3.0. Python 3.1 был выпущен 27 июня 2009 года.

PEP 372: Упорядоченные словари

Обычные словари Python перебирают пары ключ/значение в произвольном порядке. За прошедшие годы ряд авторов написали альтернативные реализации, которые запоминают порядок, в котором ключи были вставлены изначально. Основываясь на опыте этих реализаций, был введен новый класс collections.OrderedDict.

API OrderedDict практически не отличается от обычных словарей, но перебирает ключи и значения в гарантированном порядке в зависимости от того, когда ключ был вставлен первым. Если новая запись перезаписывает существующую, исходная позиция вставки остается неизменной. Удаление записи и ее повторная вставка переместит ее в конец.

Стандартная библиотека теперь поддерживает использование упорядоченных словарей в нескольких модулях. Модуль configparser использует их по умолчанию. Это позволяет читать, изменять и записывать конфигурационные файлы в исходном порядке. Метод _asdict() модуля collections.namedtuple() теперь возвращает упорядоченный словарь, значения в котором располагаются в том же порядке, что и индексы кортежей. В модуль json встраивается object_pairs_hook, чтобы декодер мог строить упорядоченные словари. Также была добавлена поддержка сторонних инструментов, таких как PyYAML.

См.также

PEP 372 - Упорядоченные словари

PEP написан Армином Ронахером и Раймондом Хеттингером. Реализация написана Раймондом Хеттингером.

Поскольку упорядоченный словарь запоминает порядок вставки, его можно использовать в сочетании с сортировкой для создания отсортированного словаря:

>>> # regular unsorted dictionary
>>> d = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2}

>>> # dictionary sorted by key
>>> OrderedDict(sorted(d.items(), key=lambda t: t[0]))
OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])

>>> # dictionary sorted by value
>>> OrderedDict(sorted(d.items(), key=lambda t: t[1]))
OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])

>>> # dictionary sorted by length of the key string
>>> OrderedDict(sorted(d.items(), key=lambda t: len(t[0])))
OrderedDict([('pear', 1), ('apple', 4), ('orange', 2), ('banana', 3)])

Новые отсортированные словари сохраняют порядок сортировки при удалении записей. Но когда добавляются новые ключи, они добавляются в конец, и сортировка не сохраняется.

PEP 378: Спецификатор формата для разделителя тысяч

Встроенная функция format() и метод str.format() используют мини-язык, который теперь включает простой, не зависящий от локали способ форматирования числа с разделителем тысяч. Это позволяет очеловечить вывод программы, улучшить его профессиональный вид и читабельность:

>>> format(1234567, ',d')
'1,234,567'
>>> format(1234567.89, ',.2f')
'1,234,567.89'
>>> format(12345.6 + 8901234.12j, ',f')
'12,345.600000+8,901,234.120000j'
>>> format(Decimal('1234567.89'), ',f')
'1,234,567.89'

Поддерживаются следующие типы: int, float, complex и decimal.Decimal.

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

См.также

PEP 378 - Спецификатор формата для разделителя тысяч

PEP написан Раймондом Хеттингером и реализован Эриком Смитом и Марком Дикинсоном.

Другие языковые изменения

Некоторые более мелкие изменения, внесенные в ядро языка Python:

  • Каталоги и zip-архивы, содержащие __main__.py файл, теперь можно выполнять напрямую, передавая их имя интерпретатору. Каталог/zip-файл автоматически вставляется в качестве первой записи в sys.path. (Предложение и первоначальный патч Энди Чу; исправленный патч Филлипа Дж. Эби и Ника Коглана; bpo-1739468).

  • Тип int() получил метод bit_length, который возвращает количество битов, необходимых для представления аргумента в двоичном виде:

    >>> n = 37
    >>> bin(37)
    '0b100101'
    >>> n.bit_length()
    6
    >>> n = 2**123-1
    >>> n.bit_length()
    123
    >>> (n+1).bit_length()
    124
    

    (При участии Фредрика Йоханссона, Виктора Стиннера, Раймонда Хеттингера и Марка Дикинсона; bpo-3439).

  • Поля в строках format() теперь могут быть автоматически пронумерованы:

    >>> 'Sir {} of {}'.format('Gallahad', 'Camelot')
    'Sir Gallahad of Camelot'
    

    Раньше для строки требовались пронумерованные поля, например: 'Sir {0} of {1}'.

    (Внесено Эриком Смитом; bpo-5237).

  • Функция string.maketrans() устарела и заменена новыми статическими методами, bytes.maketrans() и bytearray.maketrans(). Это изменение устраняет путаницу в том, какие типы поддерживались модулем string. Теперь str, bytes и bytearray имеют собственные методы maketrans и translate с промежуточными таблицами перевода соответствующего типа.

    (Внесено Георгом Брандлом; bpo-5675).

  • Синтаксис оператора with теперь позволяет использовать несколько менеджеров контекста в одном операторе:

    >>> with open('mylog.txt') as infile, open('a.out', 'w') as outfile:
    ...     for line in infile:
    ...         if '<critical>' in line:
    ...             outfile.write(line)
    

    В новом синтаксисе функция contextlib.nested() больше не нужна и теперь является устаревшей.

    (При участии Георга Брандла и Маттиаса Брандстрема; appspot issue 53094).

  • round(x, n) теперь возвращает целое число, если x - целое число. Ранее он возвращал float:

    >>> round(1123, -2)
    1100
    

    (Предоставлено Марком Дикинсоном; bpo-4707).

  • Теперь Python использует алгоритм Дэвида Гея для поиска кратчайшего представления с плавающей точкой, которое не меняет своего значения. Это должно помочь уменьшить некоторую путаницу, связанную с двоичными числами с плавающей точкой.

    Это легко понять на примере такого числа, как 1.1, которое не имеет точного эквивалента в двоичной системе с плавающей запятой. Поскольку точного эквивалента нет, выражение типа float('1.1') оценивается до ближайшего представимого значения, которым является 0x1.199999999999ap+0 в шестнадцатеричной системе или 1.100000000000000088817841970012523233890533447265625 в десятичной. Это ближайшее значение использовалось и продолжает использоваться в последующих вычислениях с плавающей запятой.

    Новым является способ отображения числа. Раньше в Python использовался простой подход. Значение repr(1.1) вычислялось как format(1.1, '.17g'), которое оценивалось в '1.1000000000000001'. Преимущество использования 17 цифр заключалось в том, что оно опиралось на гарантии IEEE-754, гарантируя, что eval(repr(1.1)) будет округляться точно до своего исходного значения. Недостатком было то, что многие люди находили вывод запутанным (ошибочно принимая внутренние ограничения двоичного представления с плавающей точкой за проблему самого Python).

    Новый алгоритм для repr(1.1) более умный и возвращает '1.1'. По сути, он перебирает все эквивалентные представления строк (те, которые хранятся с одним и тем же базовым значением float) и возвращает самое короткое представление.

    Новый алгоритм стремится выдать более чистые представления, когда это возможно, но он не меняет базовые значения. Таким образом, 1.1 + 2.2 != 3.3 все еще имеет место, даже если представления могут говорить об обратном.

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

    (При участии Эрика Смита и Марка Дикинсона; bpo-1580)

Новые, улучшенные и устаревшие модули

  • Добавлен класс collections.Counter для поддержки удобного подсчета уникальных элементов в последовательности или итерируемой таблице:

    >>> Counter(['red', 'blue', 'red', 'green', 'blue', 'blue'])
    Counter({'blue': 3, 'red': 2, 'green': 1})
    

    (Внесено Раймондом Хеттингером; bpo-1696199).

  • Добавлен новый модуль tkinter.ttk для доступа к набору тематических виджетов Tk. Основная идея ttk заключается в том, чтобы отделить, насколько это возможно, код, реализующий поведение виджета, от кода, реализующего его внешний вид.

    (Предоставлено Гильерме Поло; bpo-2983)

  • Классы gzip.GzipFile и bz2.BZ2File теперь поддерживают протокол управления контекстом:

    >>> # Automatically close file after writing
    >>> with gzip.GzipFile(filename, "wb") as f:
    ...     f.write(b"xxx")
    

    (Предоставлено Антуаном Питру.)

  • Модуль decimal теперь поддерживает методы создания десятичного объекта из двоичного float. Преобразование является точным, но иногда может вызывать удивление:

    >>> Decimal.from_float(1.1)
    Decimal('1.100000000000000088817841970012523233890533447265625')
    

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

    (Предоставлено Раймондом Хеттингером и Марком Дикинсоном).

  • В модуле itertools появились две новые функции. Функция itertools.combinations_with_replacement() - одна из четырех для генерации комбинаторики, включая перестановки и декартовы произведения. Функция itertools.compress() имитирует своего тезку из APL. Кроме того, существующая функция itertools.count() теперь имеет необязательный аргумент шаг и может принимать любой тип счетной последовательности, включая fractions.Fraction и decimal.Decimal:

    >>> [p+q for p,q in combinations_with_replacement('LOVE', 2)]
    ['LL', 'LO', 'LV', 'LE', 'OO', 'OV', 'OE', 'VV', 'VE', 'EE']
    
    >>> list(compress(data=range(10), selectors=[0,0,1,1,0,1,0,1,0,0]))
    [2, 3, 5, 7]
    
    >>> c = count(start=Fraction(1,2), step=Fraction(1,6))
    >>> [next(c), next(c), next(c), next(c)]
    [Fraction(1, 2), Fraction(2, 3), Fraction(5, 6), Fraction(1, 1)]
    

    (Предоставлено Раймондом Хеттингером.)

  • collections.namedtuple() теперь поддерживает ключевой аргумент rename, который позволяет автоматически преобразовывать недействительные имена полей в позиционные имена в виде _0, _1 и т. д. Это полезно, когда имена полей создаются внешним источником, таким как CSV-заголовок, список полей SQL или пользовательский ввод:

    >>> query = input()
    SELECT region, dept, count(*) FROM main GROUPBY region, dept
    
    >>> cursor.execute(query)
    >>> query_fields = [desc[0] for desc in cursor.description]
    >>> UserQuery = namedtuple('UserQuery', query_fields, rename=True)
    >>> pprint.pprint([UserQuery(*row) for row in cursor])
    [UserQuery(region='South', dept='Shipping', _2=185),
     UserQuery(region='North', dept='Accounting', _2=37),
     UserQuery(region='West', dept='Sales', _2=419)]
    

    (Внесено Раймондом Хеттингером; bpo-1818).

  • Функции re.sub(), re.subn() и re.split() теперь принимают параметр flags.

    (Предоставлено Грегори Смитом.)

  • Модуль logging теперь реализует простой класс logging.NullHandler для приложений, которые не используют логирование, но вызывают библиотечный код, который его использует. Установка обработчика с нулевым значением позволит избавиться от ложных предупреждений типа «Не удалось найти обработчиков для логгера foo»:

    >>> h = logging.NullHandler()
    >>> logging.getLogger("foo").addHandler(h)
    

    (Предоставлено Vinay Sajip; bpo-4384).

  • Модуль runpy, поддерживающий переключатель командной строки -m, теперь поддерживает выполнение пакетов, ища и выполняя подмодуль __main__ при указании имени пакета.

    (Предоставлено Andi Vajda; bpo-4195).

  • Модуль pdb теперь может получить доступ и отобразить исходный код, загруженный через zipimport (или любой другой соответствующий PEP 302 загрузчик).

    (Внесено Александром Белопольским; bpo-4201)

  • Объекты functools.partial теперь можно мариновать.

(Предложено Антуаном Питру и Джесси Ноллером. Реализовано Джеком Дидерихом; bpo-5228).

  • Добавьте темы справки pydoc для символов, чтобы help('@') работал как положено в интерактивной среде.

    (Внесено Дэвидом Лабаном; bpo-4739).

  • Модуль unittest теперь поддерживает пропуск отдельных тестов или классов тестов. Также поддерживается пометка теста как ожидаемого провала - теста, о котором известно, что он сломан, но который не должен быть засчитан как провал в TestResult:

    class TestGizmo(unittest.TestCase):
    
        @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
        def test_gizmo_on_windows(self):
            ...
    
        @unittest.expectedFailure
        def test_gimzo_without_required_library(self):
            ...
    

    Также были добавлены тесты на исключения для работы с менеджерами контекста, использующими оператор with:

    def test_division_by_zero(self):
        with self.assertRaises(ZeroDivisionError):
            x / 0
    

    Кроме того, было добавлено несколько новых методов утверждения, включая assertSetEqual(), assertDictEqual(), assertDictContainsSubset(), assertListEqual(), assertTupleEqual(), assertSequenceEqual(), assertRaisesRegexp(), assertIsNone() и assertIsNotNone().

    (Предоставлено Бенджамином Петерсоном и Антуаном Питру).

  • В модуле io появились три новые константы для метода seek(): SEEK_SET, SEEK_CUR и SEEK_END.

  • Кортеж sys.version_info теперь является именованным кортежем:

    >>> sys.version_info
    sys.version_info(major=3, minor=1, micro=0, releaselevel='alpha', serial=2)
    

    (Внесено Россом Лайтом; bpo-4285).

  • Модули nntplib и imaplib теперь поддерживают IPv6.

    (При участии Дерека Морра; bpo-1655 и bpo-1664).

  • Модуль pickle был адаптирован для лучшей совместимости с Python 2.x при использовании с протоколом 2 или ниже. Реорганизация стандартной библиотеки изменила формальные ссылки для многих объектов. Например, __builtin__.set в Python 2 называется builtins.set в Python 3. Это изменение затруднило попытки обмена данными между разными версиями Python. Но теперь, когда выбран протокол 2 или ниже, pickler будет автоматически использовать старые имена Python 2 как для загрузки, так и для дампа. Эта перестановка включена по умолчанию, но может быть отключена с помощью опции fix_imports:

    >>> s = {1, 2, 3}
    >>> pickle.dumps(s, protocol=0)
    b'c__builtin__\nset\np0\n((lp1\nL1L\naL2L\naL3L\natp2\nRp3\n.'
    >>> pickle.dumps(s, protocol=0, fix_imports=False)
    b'cbuiltins\nset\np0\n((lp1\nL1L\naL2L\naL3L\natp2\nRp3\n.'
    

    Досадным, но неизбежным побочным эффектом этого изменения является то, что пикули протокола 2, созданные в Python 3.1, не будут читаться в Python 3.0. При переносе данных между реализациями Python 3.x следует использовать новейший протокол pickle, протокол 3, поскольку он не пытается сохранить совместимость с Python 2.x.

    (Предоставлено Александром Вассалотти и Антуаном Питру, bpo-6137).

  • Добавлен новый модуль importlib. Он предоставляет полную, переносимую, чисто питоновскую эталонную реализацию оператора import и его аналога, функции __import__(). Это существенный шаг вперед в документировании и определении действий, происходящих при импорте.

    (Предоставлено Бреттом Кэнноном.)

Оптимизации

Добавлены значительные улучшения производительности:

  • Новая библиотека ввода-вывода (как определено в PEP 3116) была написана в основном на Python и быстро оказалась проблемным узким местом в Python 3.0. В Python 3.1 библиотека ввода-вывода была полностью переписана на C и работает в 2-20 раз быстрее в зависимости от поставленной задачи. Чистая версия Python по-прежнему доступна для экспериментов с помощью модуля _pyio.

    (Предоставлено Амори Форжо д’Арк и Антуаном Питру).

  • Добавлена эвристика, благодаря которой кортежи и массивы, содержащие только не отслеживаемые объекты, не отслеживаются сборщиком мусора. Это может уменьшить размер коллекций и, следовательно, накладные расходы на сборку мусора в длительно работающих программах, в зависимости от особенностей использования ими типов данных.

    (Предоставлено Антуаном Питру, bpo-4688).

  • При включении опции configure --with-computed-gotos в компиляторах, которые ее поддерживают (в частности, gcc, SunPro, icc), цикл оценки байткода компилируется с новым механизмом диспетчеризации, который дает ускорение до 20 %, в зависимости от системы, компилятора и бенчмарка.

    (Внесено Антуаном Питру вместе с рядом других участников, bpo-4753).

  • Декодирование UTF-8, UTF-16 и LATIN-1 теперь происходит в два-четыре раза быстрее.

    (Предоставлено Антуаном Питру и Амори Форжо д’Арк, bpo-4868).

  • Модуль json теперь имеет расширение на языке C, что значительно повышает его производительность. Кроме того, API был изменен таким образом, что json работает только с str, а не с bytes. Это изменение делает модуль близким к JSON specification, который определяется в терминах Unicode.

    (Внесено Бобом Ипполито и переведено на Py3.1 Антуаном Питру и Бенджамином Петерсоном; bpo-4136).

  • При распаковке теперь сохраняются имена атрибутов распакованных объектов. Это экономит память и позволяет уменьшать размер маринованных объектов.

    (При участии Джейка Макгуайра и Антуана Питру; bpo-5084).

IDLE

  • В меню формата IDLE теперь есть опция удаления пробельных символов из исходного файла.

    (Предоставлено Роджером Д. Серви; bpo-5150).

Изменения в сборке и C API

Изменения в процессе сборки Python и в C API включают:

  • Целочисленные числа теперь хранятся внутри либо в базе 2**15, либо в базе 2**30, причем база определяется во время сборки. Ранее они всегда хранились в base 2**15. Использование базы 2**30 дает значительный прирост производительности на 64-битных машинах, но результаты бенчмарков на 32-битных машинах были неоднозначными. Поэтому по умолчанию используется база 2**30 на 64-битных машинах и база 2**15 на 32-битных; на Unix есть новая опция configure --enable-big-digits, которая может быть использована для отмены этого значения.

    Помимо повышения производительности, это изменение должно быть незаметно для конечных пользователей, за одним исключением: для целей тестирования и отладки есть новый sys.int_info, который предоставляет информацию о внутреннем формате, указывая количество бит на цифру и размер в байтах типа C, используемого для хранения каждой цифры:

    >>> import sys
    >>> sys.int_info
    sys.int_info(bits_per_digit=30, sizeof_digit=4)
    

    (Предоставлено Марком Дикинсоном; bpo-4258).

  • Функция PyLong_AsUnsignedLongLong() теперь обрабатывает отрицательные pylong, поднимая OverflowError вместо TypeError.

    (При участии Марка Дикинсона и Лисандро Далькрина; bpo-5175).

  • Утратил силу PyNumber_Int(). Вместо этого используйте PyNumber_Long().

    (Предоставлено Марком Дикинсоном; bpo-4910).

  • Добавлена новая функция PyOS_string_to_double() для замены устаревших функций PyOS_ascii_strtod() и PyOS_ascii_atof().

    (Предоставлено Марком Дикинсоном; bpo-5914).

  • Добавлен PyCapsule в качестве замены PyCObject. API. Основное отличие заключается в том, что новый тип имеет хорошо определенный интерфейс для передачи информации о безопасности типизации и менее сложную сигнатуру для вызова деструктора. Старый тип имел проблемный API и теперь устарел.

    (Предоставлено Ларри Гастингсом; bpo-5630).

Переход на Python 3.1

В этом разделе перечислены ранее описанные изменения и другие исправления, которые могут потребовать внесения изменений в ваш код:

  • Новые представления строк с плавающей точкой могут нарушить существующие док-тесты. Например:

    def e():
        '''Compute the base of natural logarithms.
    
        >>> e()
        2.7182818284590451
    
        '''
        return sum(1/math.factorial(x) for x in reversed(range(30)))
    
    doctest.testmod()
    
    **********************************************************************
    Failed example:
        e()
    Expected:
        2.7182818284590451
    Got:
        2.718281828459045
    **********************************************************************
    
  • Автоматическая перестановка имен в модуле pickle для протокола 2 или ниже может сделать пикули Python 3.1 нечитаемыми в Python 3.0. Одно из решений - использовать протокол 3. Другое решение - установить опцию fix_imports в False. Подробнее см. обсуждение выше.