Что нового в Python 3.1.2

Статья разъясняет новые возможности Python 3.1 по сравнению с 3.0 (по релизу Python 3.1.2). Автор: Raymond Hettinger.

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

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

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

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

Смотрите так же: PEP 372 - Упорядоченные словари. PEP написали Armin Ronacher и Raymond Hettinger. Реализацию написал Raymond Hettinger.

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 - Спецификатор формата и разделитель порядков (Format Specifier for Thousands Separator). PEP написал Raymond Hettinger, реализация: Eric Smith и Mark Dickinson.

 

Другие изменения языка Python

Внесены некоторые небольшие изменения в ядро языка Python:

  • Каталоги и zip-архивы, содержащие файл __main__.py теперь выполняются непосредственно путем передачи их имени интерпретатору. Каталог/zip-файл автоматически добавляется в sys.path. (Предложение и первоначальный патч создал Andy Chu; пересмотрели патч Phillip J. Eby и Nick Coghlan; issue 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

    (Разработка Fredrik Johansson, Victor Stinner, Raymond Hettinger и Mark Dickinson; issue 3439.)

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

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

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

    (Разработка Eric Smith; issue 5237.)

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

    (Разработка Georg Brandl; issue 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() больше не требуется и не рекомендуется к использованию.

    (Разработка Georg Brandl и Mattias Brändström; appspot issue 53094.)

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

    >>> round(1123, -2)
    1100

    (Разработка Mark Dickinson; issue 4707.)

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

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

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

    Новый алгоритм для repr(1.1) значительно умнее и возвращает  '1.1'. Алгоритм находит все эквивалентные представления строки  (из тех, что хранятся с определёным значением числа с плавающей точкой) и возвращает кратчайшее представление.

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

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

    (Разработка Eric Smith и Mark Dickinson; issue 1580)

 

Новые, улучшенные и запрещённые методы

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

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

    (Разработка Raymond Hettinger; issue 1696199.)

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

    (Разработка Guilherme Polo; issue 2983.)

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

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

    (Разработка by Antoine Pitrou.)

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

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

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

    (Разработка Raymond Hettinger и Mark Dickinson.)

  • Модуль 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)]

    (Разработка by Raymond Hettinger.)

  • collections.namedtuple() теперь поддерживает ключевой аргумент rename, который автоматически преобразовать неправильные имена полей (fieldnames), добавив _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)]

    (Разработка Raymond Hettinger; issue 1818.)

  • Функции re.sub(), re.subn() и re.split() теперь принимают параметры-флаги.

    (Разработка Gregory Smith.)

  • Модуль logging представляет простой класс logging.NullHandler для приложений, которые не используют журналы, но вызывают код библиотеки, делающей это. Установка пустого обработчика позволяет избежать ложных предупреждений вида “No handlers could be found for logger foo (Обработчики не могут быть найдены для журнала foo)”:

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

    (Разработка Vinay Sajip; issue 4384).

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

    (Разработка Andi Vajda; issue 4195.)

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

    (Разработка Alexander Belopolsky; issue 4201.)

  • Объекты functools.partial могут быть теперь сериализированы.

    (Рекомендовано Antoine Pitrou и Jesse Noller. Реализация Jack Diedrich; issue 5228.)
  • Добавлен раздел справки pydoc для символов, так что help('@') работает как ожидается.

(Разработка David Laban; issue 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().

(Разработка Benjamin Peterson и Antoine Pitrou.)

  • Модуль 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)

    (Разработка Ross Light; issue 4285.)

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

    (Разработка Derek Morr; issue 1655 и issue 1664.)

  • Модуль pickle адаптирован для лучшей интероперабельности с Python 2.x при использовании протокола 2 или ниже. Реорганизация стандартной библиотеки изменила формальное описание для многих обхектов. Например, __builtin__.set в Python 2 назван в Python 3  builtins.set. Усилия по этим изменениям направлены на улучшение обмена данными между различными версиями 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.'

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

    (Разработка Alexandre Vassalotti и Antoine Pitrou, issue 6137.)

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

    (Разработано Brett Cannon.)

 

Оптимизации

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

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

    (Разработка Amaury Forgeot d’Arc и Antoine Pitrou.)

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

    (Разработка Antoine Pitrou, issue 4688.)

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

    (Разработано Antoine Pitrou наряду с многими другими участниками, issue 4753).

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

    (Разработка Antoine Pitrou и Amaury Forgeot d’Arc, issue 4868.)

  • Модуль json теперь имеет C расширение, в целях улучшения производительности. В дополнение, API было переработано , так что json работает только со str, и не работает с bytes. Эти изменения приблизили модуль к соответствию спецификации JSON, которая определена в терминах юникод.

    (Разработка Bob Ippolito, переработка в Py3.1 Antoine Pitrou и Benjamin Peterson; issue 4136.)

  • Сериализации теперь содержат атрибут с именами сериализированных объектов. Это сохраняет память и позволяет сериализациям быть меньше.

    (Разработка Jake McGuire и Antoine Pitrou; issue 5084.)

IDLE

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

    (Разработка Roger D. Serwy; issue 5150.)

 

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

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

  • Целые числа теперь хранятся внутренне по базе 2**15, либо 2**30, база определяется при билде. Первоначально, целые всегда сохранялись по базе 2**15. Использование базы 2**30 дало значительное улучшение производительности на 64-битных машинах, результаты тестов производительности на 32-битных машинах неоднозначны. Поэтому на 64-битных машинах по умолчанию база устанавливается 2**30, и 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)

    (Разработка Mark Dickinson; issue 4258.)

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

    (Разработка Mark Dickinson и Lisandro Dalcrin; issue 5175.)

  • Запрещено PyNumber_Int(). Используйте взамен PyNumber_Long().

    (Разработка Mark Dickinson; issue 4910.)

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

    (Разработка Mark Dickinson; issue 5914.)

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

    (Разработка Larry Hastings; issue 5630.)

 

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

Этот раздел содержит список ранее описанных изменений и других исправлений ошибок, которые могут потребовать изменить Ваш код, для перехода к 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
    **********************************************************************
  • Автоматическое переопределение имён в модуле сериализации для протокола 2 или ниже может сделать сериализации Python 3.1 нечитаемыми в Python 3.0. Первое решение - использовать протокол 3. Другое возможное решение: установить опцию fix_imports в False.  

Перевод статьи What’s New In Python 3.1 выполнил Python-хостинг КОМТЕТ