Что нового в 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 написан Армином Ронахером и Раймондом Хеттингером. Реализация написана Раймондом Хеттингером.

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)
    

    (Внесено Винаем Саджипом; 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.'
    

    Досадным, но неизбежным побочным эффектом этого изменения является то, что pickle протокола 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).

Изменения в API Build и C

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

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

Вернуться на верх