python

Python 3000

Dmitry Vasiliev 17:14, 2009 3 24 18:54, 2007 6 20

Python 3000 - это имя проекта по созданию новой ветки развития Python (не путать с Perl 6 ;-) Первые идеи по проекту появились в 2000-м году и имя было дано по следам Windows 2000. Недавно Гуидо Ван Россум (Guido van Rossum) написал обновление по статусу проекта и по следам этого обновления я рассмотрю что нового нас ожидает в новой версии Python.

Первую альфа версию Python 3.0 (кодовое имя проекта - "Python 3000" или "Py3k") планируется выпустить к осени 2007 года и где-то через год первую стабильную версию. Основная цель проекта - избавиться от старого балласта, от которого невозможно избавиться поддерживая обратную совместимость с более старыми версиями Python. Таким образом Python 3.0 не будет обратно совместим по многим пунктам, но для упрощения перехода существуют следующие возможности:

  • Следующая стабильная версия Python 2.6 будет поддерживать "режим предупреждений Py3k" который может предупреждать о возможностях перестающих работать в Python 3000;
  • Python 2.6 будет поддерживать многие возможности Py3k, например через выражение __future__;
  • Также разрабатывается автоматический конвертер который может конвертировать старый код в код для Py3k;

Что нового в Python 3.0

Итак рассмотрим наиболее важные изменения которые уже реализованы, или будут в скором времени реализованы для Python 3.0.

Unicode строки и массивы байтов

В Python 3.0 все текстовые строки будут хранить Unicode, а для бинарных данных будет отдельный тип bytes. Для любителей Java такая модель должна быть знакома, если смотреть на старые версии Python то можно грубо сказать, что старый тип unicode теперь становиться str, а старый str превращается в bytes. В отличие от старого типа str, bytes не содержит методов связанных с текстом (таких как upper/lower) и будет изменяемым типом больше похожим на list:

>>> b = b"test"
>>> b
b'test'
>>> b.insert(2, ord("-"))
>>> b
b'te-st'
>>> b.append(0)
b'te-st\0'

Также кодировкой по умолчанию для исходных файлов теперь будет UTF-8 вместо ASCII и будет возможность использовать не только латинские символы в именах идентификаторов/функций/классов и др.:

def сортировать_массив(массив):
    return sorted(массив)

Новая библиотека ввода/вывода

В связи с изменениями связанными с Unicode и желанием избавиться от зависимости от библиотеки C stdio теперь в Python будет новая библиотека ввода/вывода io. Библиотека разбита на слои и похоже также навеяна Java: на нижнем уровне классы работающие с не буферизуемыми данными, выше - классы работающие с буферизованными данными и еще выше классы для работы с текстовыми данными. Таким образом файлы открытые в бинарном режиме (используя "rb" или "wb") будут работать с типом bytes, а файлы открытые в текстовом режиме будут работать с Unicode строками.

Вывод на консоль

Выражение print превратилось в функцию print() которая может принимать именованные аргументы для управления выводом:

>>> print("test", "test")
test test
>>> print("test", "test", sep=".", end="$\n")
test.test$
>>> import sys
>>> print("error", file=sys.stderr)
error

Теперь для форматирования данных при выводе вместо оператора % будет использоваться метод строк format() (см. PEP3101), который использует фигурные скобки для определения переменных и который также может быть настроен для каждого объекта с помощью метода __format__():

>>> "test".format()
'test'
>>> "variables: {0}, {a}".format(10, a=20)
'variables: 10, 20'
>>> ";-{{}}".format()
';-{}'
>>> format("{0.append}, {1[name]}", [], {"name": "Bob"})
'<built-in method append of list object at 0xb7d238ac>, Bob'

>>> class Echo:
...     def __format__(self, specifiers):
...         return specifiers
...
>>> "{0}".format(Echo())
''
>>> "{0:test}".format(Echo())
'test'

Изменения связанные с классами

Так называемые "классические классы" уходят и теперь для создания классов "нового типа" не надо наследоваться от object:

>>> class Test:
...     pass
...
>>> type(Test)
<type 'type'>

При желании функции/методы могут аннотироваться с помощью следующего синтаксиса:

>>> def test(a: int) -> int:
...     pass
...
>>> test.__annotations__
{'a': <type 'int'>, 'return': <type 'int'>}

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

>>> def marker(klass):
...     klass.decorated = True
...     return klass
>>> @marker
... class Marked:
...     pass
...
>>> Marked.decorated
True

Определить мета-класс для класса теперь можно с помощью следующего, более читаемого, синтаксиса:

>>> class Test(metaclass=type):
...     pass
...

Также можно передавать другие именованные параметры при создании класса и они будут переданы методу мета-класса __new__().

Новый метод мета-класса __prepare__() (см. PEP-3115) позволяет изменять словарь который будет использоваться для хранения всех элементов класса во время обработки тела класса:

>>> class StringGetter(dict):
...     def __getitem__(self, name):
...         return str(super(StringGetter, self).__getitem__(name))
...
>>> class StringMeta(type):
...     @staticmethod
...     def __prepare__(name, bases, **kwargs):
...         return StringGetter()
...
>>> class Strings(metaclass=StringMeta):
...     a = 100
...     b = 200
...
>>> Strings.a
'100'
>>> Strings.b
'200'
>>>

Базовые классы в Python 3000 могут быть определены динамически:

>>> class A:
...     pass
...
>>> class B:
...     pass
...
>>> bases = (A, B)
>>> class C(*bases):
...     pass
...
>>> C.mro()
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>]

Проверки isinstance() и issubclass() теперь могут быть переопределены созданием методов класса __instancecheck__() или __subclasscheck__() соответственно:

>>> class GoodFriend:
...     @classmethod
...     def __instancecheck__(cls, instance):
...         return True
...     @classmethod
...     def __subclasscheck__(cls, subclass):
...         return True
...
>>> isinstance(1, GoodFriend)
True
>>> issubclass(dict, GoodFriend)
True

Появились необязательные абстрактные классы которые можно использовать для описания поведения объектов и также как базовую функциональность. Можно также определять свои базовые классы с помощью мета-класса ABCMeta и абстрактные методы для них с помощью декоратора abstractmethod:

>>> from collections import MutableMapping
>>> from abc import abstractmethod
>>> class AbstractSortedMapping(MutableMapping):
...     @abstractmethod
...     def sortedkeys(self):
...         pass
...
>>> class SortedMapping(dict, AbstractSortedMapping):
...     def sortedkeys(self):
...         return sorted(self)
...

Исключения

Строковые исключения окончательно исчезают и все исключения теперь обязательно должны иметь супер-классом BaseException, также желательно, что бы все пользовательские исключения наследовались от Exception. Исключения больше не ведут себя как последовательности, вместо этого добавлен атрибут args содержащий последовательность аргументов переданных в конструктор. Синтаксис выражения except E, e: изменен на except E as e. Переменная используемая в выражении except автоматически удаляется при выходе из блока except. Вместо sys.exc_info() теперь желательно использовать переменный исключения: __class__ - тип исключения, __traceback__ - путь вызовов, __context__ - предыдущее исключение которое маскируется текущим исключением (см. PEP-3134):

>>> class TestException(Exception):
...     pass
...
>>> TestException.mro()
[<class '__main__.TestException'>, <type 'Exception'>, <type 'BaseException'>, <type 'object'>]
>>> try:
...     raise TestException("test")
... except TestException as e:
...     print(e.args)
...
('test',)

Целые

Вместо двух типов int и long теперь остается только один int ведущий себя как long из Python 2.x. Оператор деления / теперь возвращает float, для целочисленного деления нужно использовать //:

>>> i = 10 ** 20
>>> i
100000000000000000000
>>> type(i)
<type 'int'>

>>> 10 / 3
3.3333333333333335
>>> 10 // 3
3

Константы в восьмеричной, шестнадцатеричной и двоичной системах могут быть определены с новым синтаксисом:

>>> 0x10
16
>>> 0o10
8
>>> 0b10
2

Итераторы и итерируемые объекты вместо списков

Методы словарей keys(), items() теперь возвращают виртуальные множества для ключей и пар (ключ, значение) соответственно, метод values() возвращает виртуальный итерируемый контейнер. Методы iter*() удалены:

>>> d = {"a": 1, "b": 2}
>>> keys = d.keys()
>>> keys
<dict_keys object at 0xb7dbb570>
>>> d.items()
<dict_items object at 0xb7dbb520>
>>> d.values()
<dict_values object at 0xb7dbb550>
>>> "a" in keys
True
>>> ("b", 2) in d.items()
True
>>> "c" in keys
False
>>> d["c"] = 3
>>> "c" in keys
True

Функции range(), zip(), map(), filter() теперь возвращают итерируемые значения:

>>> range(10)
range(10)
>>> zip(range(10), range(10))
<zipiterator object at 0xb7cf9e8c>
>>> map(zip, (range(10), range(10)))
[<zipiterator object at 0xb7cf9f0c>, <zipiterator object at 0xb7cf9fcc>]

Разное

Сравнения (<, <=, >, =>) теперь по умолчанию выкидывают TypeError, (==, !=) - сравнивают объекты на идентичность:

>>> class Test:
...     pass
...
>>> t1 = Test()
>>> t2 = Test()
>>> t1 > t2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: Test() > Test()
>>> t1 == t2
False
>>> t1 != t2
True

Ключевое слово nonlocal позволяет изменять переменные во внешнем (не глобальном) пространстве имен:

>>> def generator():
...     a = 0
...     def increment():
...         nonlocal a
...         a += 1
...         return a
...     return increment
...
>>> i = generator()
>>> i()
1
>>> i()
2
>>> i()
3