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

Автор:Рэймонд Хеттингер

В данной статье объясняются новые возможности Python 3.1 по сравнению с 3.0.

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

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

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

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

См.также

PEP 372 — Упорядоченные словари
PEP, написанный Армином Ронахером и Рэймондом Хеттингером. Реализация автор Раймонд Хеттингер.

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() больше не нужна и устарела.

    (Предоставлено Георгом Брандлом и Маттиасом Брандстрёмом; проблема 53094 на appspot.)

  • round(x, n) теперь возвращает целое число, если x является целым числом. Ранее она возвращала число с плавающей запятой:

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

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

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

    (Предоставлено Эриком Смитом и Марком Дикинсоном; 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 теперь поддерживают протокол управления контекстом:

    >>> # Автоматически закрывать файл после записи
    >>> 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() теперь имеет необязательный аргумент step и может принимать любой тип последовательности счета, включая 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() теперь принимают параметр флагов.

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

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

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

    (Предоставлено Винаем Саджипом; bpo-4384).

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

    (Предоставлено Энди Вайдой; 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 или ниже, сборщик будет автоматически использовать старые имена 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. Последний протокол pickle, протокол 3, следует использовать при переносе данных между реализациями Python 3.x, поскольку он не пытается оставаться совместимым с Python 2.x.

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

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

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

Оптимизации

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

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

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

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

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

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

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

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

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

  • У модуля json теперь есть расширение C, что существенно вызывает его производительность. Кроме того, API был изменён так, что json работает только с str, а не с bytes. Это изменение делает модуль более близким к JSON спецификации, которая определена в терминах Юникода.

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

  • Анпиклинг (Unpickling) теперь интернирует имена атрибутов пиклинг объектов. Это экономит память и позволяет уменьшить размер пиклей.

    (Предоставлено Джейком Макгуайром и Антуаном Питру; bpo-5084.)

ПРАЗДНЫЙ IDLE

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

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

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

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

  • Целые числа теперь хранятся внутри либо по основанию 2**15 или по основанию 2**30, причем база определяется во время сборки. Раньше они всегда хранились по основанию 2**15. Использование основания 2**30, что даёт значительный прирост производительности на 64-битных машинах, но результаты тестов на 32-битных машинах были неоднозначными. Поэтому по умолчанию используется основание 2**30 на 64-битных машинах и основание 2**15 на 32-разрядных машинах; в Unix есть новый параметр конфигурации --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 в качестве замены API PyCObject. Принципиальное отличие состоит в том, что новый тип имеет четко определённый интерфейс для передачи информации о безопасности ввода и менее сложную сигнатуру для вызова деструктора. Старый тип имел проблемный API и теперь устарел.

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

Портирование на Python 3.1

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

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

    def e():
        '''Вычислить основание натуральных логарифмов.
    
        >>> 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. Смотрите обсуждение выше для более подробной информации.