17 апреля 2001 года вышел Python 2.1. Несмотря на то, что младший номер версии изменился всего лишь на единицу, количество дополнений в языке и библиотеках сравнимо с тем, что было внесено в 2.0 по сравнению с 1.6 (впрочем, основной причиной того, что после версии 1.6 вышла 2.0, а не 1.7, была необходимость четко отделить все последующие версии, от версий, выходившие под эгидой CNRI).
Одно из самых крупных изменений касается не самого языка, а процесса его разработки. Хотя (к счастью) Python сохранил "диктаторскую" модель разработки, когда автор языка имеет абсолютно решающее слово в принятии или непринятии любых предложений по расширению/изменению языка или библиотек, сама модель внесения, обсуждения и принятия таких предложений изменилась, став упорядоченной и формализованной. Она основана на PEP (Python Enhancement Proposals - предложения по расширению Python). PEP - документ, описывающий предлагаемое новое свойство языка (Standard Track PEP) или предоставляющий некоторую существенную информацию сообществу пользователей Python (Informational PEP). Примером последнего может служить PEP0001 - PEP Purpose and Guidelines, описывающий предназначение и механизм PEP. Категоризованный индекс всех PEP находится на http://python.sourceforge.net/peps/. Python 2.1 - первая версия, полностью разработанная на основе модели PEP.
Это изменение касается способа, которым в Python разрешаются имена внутри функций. Язык Python имеет блочную структуру, сходную с языками алгольного типа (к которым, с некоторыми оговорками, относится и C). Базовой единицей (областью) программы являются модуль, определение класса или функция. Статически вложенные пространства имен определяют порядок поиска имени, встретившегося в блоке, как последовательный поиск, начиная с ближайшего лексически объемлющего блока и по направлению ко все более внешним.
В Python 2.0 (и ранее) любое имя, на которое появлялась ссылка в функции, последовательно искалось в трех пространствах имен:
Это правило поиска иногда называют LGB (Local, Global, Builtin). При этом, для вложенных функций (т.е, функций определенных внутри других функций), поиск имен производился точно таким же образом, т.е., если имя не было найдено непосредственно в пространстве имен вложенной функции, оно сразу начинало искаться в глобальном пространстве, а не в объемлющей функции. Это отличалось от логики других блочных языков, где поиск производится последовательно во всех объемлющих областях.
# Пример разрешения имени во вложенной
функции
x = 'Глобальная область'
def external():
x = 'Область функции "external()"'
def nested():
print "Внутри nested()
x=='%s'" % x
nested()
external()
Вышеприведенный пример выдает для Python версии 2.0 или 1.5:
Внутри nested() x=='Глобальная область'
Такая логика приводит к следующим неприятностям:
lambda
ограничивается, поскольку единственным
способом передать в lambda-функцию элемент
контекста объемлющей функции
оказывается неестественная и уродливая
конструкция с применением параметров по
умолчанию;# Пример использования lambda без
вложенного пространства имен
def incrlist(lst, incr):
# Внутри labmbda переменная incr не
видна, поэтому передаем ее как
умалчиваемый параметр
map(lambda x, incr = incr: x + incr, lst)
Введение в версии 2.1 сходных с другими
блочными языками правил разрешения имен во
вложенных областях призвано решить эти
проблемы. Но из-за того, что новые правила
могут вызвать проблемы с обратной
совместимостью, вложенные пространства
имен введены как опциональное свойство (__future__
)
с именем nested_scopes. В версии 2.2 это
свойство станет обязательным.
# Включить вложенные пространства имен
from __future__ import nested_scopes
Таким образом, если nested_scopes включено, имя последовательно ищется во всех объемлющих контекстах (кроме контекста класса), пока не будет найдено. Контекст класса исключен, чтобы не нарушать единобразия правил доступа к атрибутам в Python, которые требуют, чтобы объект, к атрибуту которого нужен доступ, задавался всегда. В результате вышеприведенный пример выдаст:
а вышеприведенный пример с конструкцией lambda
можно записать естественным образом:
def incrlist(lst, incr):
map(lambda x: x + incr, lst)
Если nested_scopes выключено, Python использует старую семантику пространств имен, но выдает предупреждения обо всех участках кода, смысл которых изменится при применении новой семантики. Это дает возможность исправить такой код до выхода версии 2.2 (если вы, конечно, не предпочитаете навсегда остаться с версией 2.1 или 2.0).
Механизм введения новых свойств в язык
Разработчики языка Python традиционно
стараются вносить изменения в язык таким
образом, чтобы не затронуть совместимость с
программами на Python, написанными для
предыдущих версий. Но все же время от
времени приходится вносить несовместимые
изменения - чаще всего в тех случаях,
когда исправляется какой-нибудь застарелый
огрех дизайна языка, или (много реже!) если
совместимость оказывается
непреодолимым препятствием для
внесения в язык абсолютно необходимого
свойства. Для того, чтобы смягчить
последствия таких изменений и облегчить
переход на новые версии, в язык было
добавлено соглашение, позволяющее включать
такие свойства опционально, но лишь на
переходный период в одну или несколько
версий, после которого они становятся
обязательными. Для этого используется
директива __future__
.
Директива __future__
- обычный оператор
импорта, использующий зарезервированное
имя модуля __future__:
from __future__ import feature
где feature - имя включаемого свойства.
Директива __future__
- конструкция
времени компиляции, а не выполнения,
поскольку она может влиять на генерацию
байткода и даже на восприятие синтаксиса
компилятором. Поэтому в тексте модуля перед
этой конструкцией могут находиться только
комментарии и строка документации (и,
естественно, другие утверждения __future__
).
При этом для любого данного релиза языка
все допустимые имена feature (т.е. имена
опциональных свойств) строго определены, и
при использовании недопустимого имени
выдается ошибка компиляции1,2.
Для любого свойства, чреватого серьезной
несовместмостью, задается переходный
период (выраженный в терминах номеров
версий), в течение которого оно является
опциональным. По истечении этого периода
оно становится обязательной частью языка (т.е.
присутствует вне зависимости от
утверждения __future__
). Например, статически
вложенные пространства имен -
опциональное свойство в версии 2.1, но,
начиная с версии 2.2 - обязательное.
# Включить вложенные пространства имен
from __future__ import nested_scopes
В течение переходного
периода,
если свойство не включено, для
конструкций, могущих изменить смысл после
его включения, выдается предупреждение.
После того, как переходный
период для для некоторого
свойства истекает, директива __future__
с именем этого свойства просто
игнорируется компилятором. Это
обеспечивает полную "прямую
совместимость" с будущими версиями
языка для исходников, использующих __future__
.
Помимо механизма выдачи сообщений об ошибках, роль которого в Python обеспечивает обработка исключений, во многих языках существует также механизм выдачи предупреждений. Предупреждение, в отличие от исключения. не является фатальным, но сигнализирует о возможной ошибке программирования, неоднозначности либо об использовании устаревшего (устаревающего) свойства языка.
Начиная с версии 2.1, в Python также вводится
механизм предупреждений. Его главное
предназначение - сообщать программисту об
использовании устаревших возможностей, а
также возможностей, которые могут быть изменены
или удалены в будущей версии языка (см. __future__
).
Например, в версии 2.1 не рекомендуется
использовать модуль regex (собственно, он
устарел еще в версии 2.0):
Предупреждения могут выдаваться как во
время компиляции, так и во время выполнения.
При этом в предупреждении присутствуют имя
модуля и строка, откуда оно выдано. Функциональность выдачи предупреждения
обеспечивает модуль warnings
, причем
пользовательские модули также могут
использовать его для выдачи собственных
предупреждений. Вводится также C API
для выдачи предупреждений из расширений и
программ со встроенным интерпретатором (PyErr_Warn()
)3.
Все предупреждения делятся на категории. Категории предупреждений составляют иерархию классов, наподобие исключений, с корневым классом Warning. Класс Warning, в свою очередь, производный от класса Exception, что позволяет преобразовывать предупреждения в исключения, если это необходимо (см. ниже описание фильтрации сообщений). На сегодняшний день стандартная иерархия категорий предупреждений следующая:
UserWarning | базовая категория по умолчанию для warning.warn() |
DeprecationWarning | базовая категория для сообщений об отменяемых/устаревших свойствах языка |
SyntaxWarning | базовая категория для предупреждений об использовании сомнительного синтаксиса |
RuntimeWarning | базовая категория для предупреждений об использовании сомнительных возможностей времени выполнения (например, зависящих от реализации) |
Как и в случае с обработкой исключений, при фильтрации базовая категория включает в себя все производные.
Чтобы выдать предупреждение из програмы на Python, следует пользоваться функцией
warnings.warn(message[, category[, stacklevel]]))
,где message - собственно строка сообщения; category - категория сообщения, производная от Warning (по умолчанию - UserWarning); stacklevel - относительный уровень стека, для которого выдается сообщение.
# Пример выдачи предупреждения
import warnings
warnings.warn("Свойство X зависит от реализации",
RuntimeWarning)
Параметр stacklevel очень полезен для создания общих функций для выдачи предупреждений. Например:
# В этом примере stacklevel = 2 приводит к
тому, что в выданном предупреждении
присутствуют
# имя модуля и строка, откуда была _вызвана_
функция implementation_dependent(). Если бы этот
# параметр не был задан, в предупреждении
присутствовали бы модуль и строка где _определена_
# функция implementation_dependent(), что сделало бы ее
определение бессмысленным!
#
def implementation_dependent(feature):
warnings.warn("Результат %s зависит от
реализации!" % feature, RuntimeWarning, stacklevel = 2)
Несмотря на то, что, как правило, программисту нужно видеть все предупреждения (для того, чтобы довести код до такого состояния, что они исчезнут), бывают случаи, когда их необходимо подавлять. Так, например, может случиться. если предупреждение выдается из "чужого" кода, или если сроки не позволяют исправить код немедленно, а промышленная версия не должна бомбардировать пользователя предупреждениями, не имеющими к нему отношения. При этом просто отключить выдачу всех предупреждений нельзя - могут оказаться подавленными необходимые. С другой стороны, некоторые предупреждения разумно интерпретировать как ошибки - например, если код модифицируется с целью очистки от многолетних наслоений, имеет смысл интерпретировать все DeprecationWarning как исключения - это позволит быстро выловить весь устаревший код. Для этого механизм предупреждений поддерживает фильтрацию.
Фильтрация позволяет задать реакцию языка на предупреждения, отобранные по заданным критериям. Критериями могут выступать:
Фильтр может использовать один критерий или их произвольную комбинацию. Фильтры задаются функцией
warnings.filterwarnings(
action[,
message[, category[, module[,
lineno[, append]]]]])
. Ниже приведены примеры фильтров:
Вы перешли с версии 1.5.2 на 2.1. Все превосходно продолжает работать, но ваша программа использует устаревшие модули regex и TERMIOS, поэтому выдает предупреждения при каждом запуске. Чтобы не пугать пользователей, вы добавляете в промышленную версию код, подавляющий все предупреждения об устаревших модулях:
import warnings
warnings.filterwarnings(action = "ignore", category =
DeprecationWarning)
Тем временем, вы хотите вычистить все
места, где используется TERMIOS, но решаете, что
переходить с regex на re слишком сложно, и
добавляете в отладочную версию следующий
код:
import warnings
# Считать предупреждения о TERMIOS ошибками
warnings.filterwarnings(action = "error", category =
DeprecationWarning, module = "TERMIOS")
# Подавлять предупреждения о regex
warnings.filterwarnings(action = "ignore", message = ".*regex
module.*" category = DeprecationWarning)
Фильтры предупреждений могут также задаваться из командной строки Python с помошью параметра -W filter, где filter задается в виде action:message:category:module:lineno. Любой элемент фильтра может быть опущен:
Слабые ссылки - новый тип данных, введенный в Python 2.1, позволяющий решить проблему циклических ссылок и динамического кеширования. Слабые ссылки позволяют ссылаться на объект, не увеличивая его счетчик ссылок.
Все обычные ссылки в Python - "жесткие" - владеющие объектом, на который ссылаются. Управление памятью и временем жизни объектов в Python осуществляется подсчетом ссылок. Это означает, что любой объект живет до тех пор, пока на него есть хотя бы одна ссылка (и уничтожается немедленно при удалении последней ссылки). Это очень удобно - с одной стороны, позволяет не заботиться об управлении памятью, с другой - обеспечивает детерминированное время жизни объекта (в отличие, например, от Java). Но такая модель не в состоянии решить ряд проблем:
def create_n_cache(param, cache = {}):
try:
# Проверим, есть ли в кеше искомое значение
retval = cache[param]
except KeyError:
cache[param] = retval =
_really_create()
return retval
Механизм слабых ссылок позволяет решить вышеупомянутые (и многие другие) проблемы. Слабая ссылка - объект, указывающий на объект Python, но не увеличивающий его счетчик ссылок. Не на все объекты можно создать слабую ссылку; можно создавать слабые ссылки на объекты классов, функции (написанные на Python), методы (связанные и несвязанные).
Модуль weakref предоставляет два вида ссылок - собственно ссылку (reference) и делегатор (proxy). Ссылку на объект можно получить, вызвав конструктор
weakref.ref(object[, callback])
.Параметр callback может задавать функцию, которая будет вызвана перед удалением объекта, на который указывает ссылка.
# Создать слабую ссылку на объект
import weakref
from UserList import UserList
object = UserList([1, 2, 3, 4])
wr = weakref.ref(object)
# Чтобы получить реальный объект, надо
разыменовать ссылку, просто вызвав ее как
функцию
print "reference==%r object==%r" % (wr, wr())
# Удаляем реальный объект. Если объект, на
который указывает слабая ссылка, удаляется,
# разыменование ссылки возвращает None
del object
print "reference==%r object==%r" % (wr, wr())
Пример выведет следующее:
Делегатор (proxy reference) - слабая ссылка,
ведущая себя (настолько, насколько у нее
получается) как объект, на который она
ссылается, т.е. доступ к атрибутам
делегатора перенаправляется к атрибутам
исходного объекта. Делегатор создается
вызовом weakref.proxy(object[, callback])
:
# Создать слабую ссылку-proxy на объект
import weakref
from UserList import UserList
object = UserList([1, 2, 3, 4])
proxy = weakref.proxy(object)
print "reference==%r" % proxy
# Делегатор ведет себя так же, как исходный
объект
print "object[0]==%s, object[-1]==%s" % (proxy[0], proxy[-1])
# Удаляем реальный объект. Если объект, на
который указывает делегатор, удаляется,
# делегатор указывает на None (и ведет себя
соответственно)
del object
# Попытка индексировать None вызовет бурное
возмущение интерпретатора
print "object[0]==%s, object[-1]==%s" % (proxy[0], proxy[-1])
Как и ожидалось, пример завершается с ошибкой:
Модуль weakref определяет класс, отлично подходящий для создания кешей - WeakValueDictionary. Это словарь, ссылки на значения в котором - слабые. При уничтожении объекта, на который есть ссылка в таком словаре, соответствующий вход в словаре (ключ и значение) автоматически удаляются. Функция create_n_cache() с использованием WeakValueDictionary могла бы выглядеть так:
import weakref
def create_n_cache(param, cache = weakref.WeakValueDictionary()):
# Проверим, есть ли в кеше искомое значение
retval = cache.get(param)
if not retval:
cache[param] = retval =
_really_create()
return retval
Значительно модифицирован механизм приведения числовых типов для расширений на C.
Раньше требовалось, чтобы бинарные числовые операции всегда получали аргументы одинакового типа, и поэтому PyNumber_Coerce() всегда автоматически вызывалась перед вызовом соответствующей операции. Теперь модуль расширения может установить флаг Py_TPFLAGS_CHECKTYPES среди флагов структуры PyTypeObject чтобы показать, что данный тип поддерживает новую модель приведения. При этом PyNumber_Coerce() не будет вызываться вообще, а операнды будут передаваться числовым операциям, определенным в этом типе, "как есть". Тем самым ответственность за обработку операндов разных типов перекладывается непосредственно на операцию (естественно, первый операнд всегда будет иметь заданный тип, поскольку это self). В том случае, если операция не может обработать операнд заданного типа, она может вернуть указатель на специальный глобальный объект - Py_NotImplemented. В этом случае интерпретатор вызывает симметричную операцию другого операнда. Если и эта операция возвращает Py_NotImplemented, возбуждается исключение. Этот алгоритм можно представить следующим псевдокодом на Python:
# Псевдокод, описывающий выполнение
бинарных арифметических операций для новых
правил приведения типов
def binary_operation(o1, o2):
result = o1.__binary_operation__(o2)
if result is NotImplemented:
result = o2.__binary_operation__(o1)
if result is NotImplemented:
raise TypeError,
"операция не реализована для данных
типов операндов"
return result
Если у типа не установлен флаг Py_TPFLAGS_CHECKTYPES, к нему применяются старые правила приведения (с вызовом PyNumber_Coerce()), что позволяет обеспечить полную обратную совместимость.
Новый механизм сравнения позволяет определять отдельные функции для реализации разных операций сравнения (<, >, <=, >=, ==, !=) как для классов, так и для расширений на С. Кроме того, он позволяет определять операции сравнения, возвращающие значения, тип которых отличен от булевского.
Стандартный механизм сравнения Python предусматривает реализацию операций сравнения для класса при помощи одной функции - __cmp__ (или, соответственно, слота tp_compare для типа расширения). Эта функция должна возвращать -1, 0 или 1 (меньше, равно, больше соответственно) и возвращать булевское значение. Такой подход прост, но обладает несколькими серьезными недостатками:
Расширенный механизм сравнения позволяет
преодолеть эти ограничения, вводя
возможность определения отдельных
операций сравнения. В классе могут быть
определены следующие методы (все или любой
набор)5,6:
== | __eq__ |
!= | __ne__ |
< | __lt__ |
> | __gt__ |
<= | __le__ |
>= | __ge__ |
Функции расширенного сравнения могут возвращать значения любых типов.
Расширенное сравнение подчиняется следующим правилам:
В Python 2.1 функции могут иметь словарь атрибутов, наподобие классов или модулей. Тем самым функциям, написанным на Python, могут присваиваться произвольные пользовательские атрибуты. Это изменение находится в общем русле эволюции Python в сторону обобщения, поскольку делает функции в большей степени "гражданами первого сорта".
В предыдущих версиях функции также имели набор атрибутов (__doc__ AKA func_doc, __name__ AKA func_name, func_code, func_defaults, func_globals), но этот набор фиксирован, только часть атрибутов могут быть записываемыми (__doc__, func_code, func_defaults), при этом только один из записываемых атрибутов обладает более-менее "произвольной" семантикой, позволяющей связать с функцией некоторую произвольную информацию - __doc__ . Это приводит к тому, что "строка документации" используется разными системами для множества разных целей, не имеющих к документации отношения. Например, Web-сервер Zope использует этот атрибут для описания "публикуемого" интерфейса; в системе разбора небольших языков SPARK в атрибут __doc__ помещаются правила разбора, etc. Такой подход неудачен по нескольким причинам:
Чтобы решить эти проблемы, к объекту
функции добавлен стандартный атрибут __dict__ и
возможность добавления произвольных
пользовательских атрибутов. Синтаксис манипуляций с
атрибутами функции такой же, как с
атрибутами объекта класса, т.е. первое
присваивание атрибуту создает его, del
-
удаляет из словаря, etc.
# Пример создает атрибуты published и return_type
функции cvt()
def cvt(s):
"""Функция возвращает
свой аргумент, преобразованный к
плавающему и деленый пополам"""
return float(s) / 2.0
s.published = 1
s.return_type = type(1.0)
В отличие от класса или объекта класса,
атрибуту __dict__ функции можно явно присвоить
значение, но оно должно быть либо реальным
словарем (т.е., например, UserDict присвоить
нельзя), либо None
. В последнем случае все
пользовательские атрибуты функции
удаляются7.
# Пример создает атрибуты published и return_type
функции cvt()
def cvt(s):
"""Функция возвращает
свой аргумент, преобразованный к
плавающему и деленый пополам"""
return float(s) / 2.0
s.__dict__ = { "published":1, "return_type": type(1.0) }
Методам класса (как связанным (bound), так и свободным (unbound)) атрибуты непосредственно присвоить нельзя, но можно присвоить атрибуты функции, являющейся реализацией метода5:
class A:
def a(): pass
# !!!! ОШИБКА !!!!
A.a.published = 1
# Правильно
A.a.im_func.published = 1
Следует заметить, что атрибуты можно присваивать только функциям, написанным на Python; функции расширений этого делать не позволяют.
Добавлен слот sys.excepthook,
позволяющий переопределить обработчик
исключений верхнего уровня. Значением
этого слота должна быть функция,
принимающая тип исключения, его значение и
traceback. Эта функция вызывается, если
исключение не обработано ни одним блоком try...except
, позволяя, таким образом,
переопределить реакцию на необработанное
исключение.
Добавлен слот sys.displayhook, позволяющий переопределить вывод значений в интерактивном режиме интерпретатора. Интерактивный режим выполняется в режиме REPL - Read-Evaluate-Print Loop. По умолчанию в этом режиме результат выполнения evaluate выводится с помощью функции repr(). sys.displayhook позволяет переопределить это поведение. Значением sys.displayhook должна быть функция (точнее, любой вызываемый объект). Эта функция вызывается в качестве print-стадии цикла REPL с параметром, являющимся результатом выполнения введенной команды (стадия evaluate). Код этой функции по умолчанию эквивалентен следующему:
import __builtin__
def displayhook(o):
if o is None:
return
__builtin__._ = None
print `o`
__builtin__._ = o
Пример функции вывода:
# Эта функция заменяет стандартный
sys.displayhook
# Очень красиво выводит сложные структуры
данных (словари/списки/кортежи)
# Имеет смысл поместить ее в sitecustomize.py
import __builtin__, sys
from pprinter import pprint
def pretty_display(object):
if object is None: return
__builtin__._ = None
pprint(object)
__builtin__._ = object
sys.displayhook = pretty_display
from ... import *
.
В модуле может быть определен список __all__,
элементами которого должны быть имена,
определенные в модуле. from ... import *
будет импортировать только то, что
перечислено в этом списке (правило, в
соответствии с которым from ... import *
игнорирует имена, начинающиеся с
подчеркивания, остается); from M import N
работает теперь
даже в том случае, если sys.modules['M'] - не
модуль. В общем случае, эта операция
эквивалентна коду:N = getattr(sys.modules['M'], 'name')
PyImport()
C API
реализован через вызов PyImport_ImportModule()
.
В результате все вызовы импорта из
программ на C/C++ учитывают перехват
импорта (например, через imputil, etc.)inspect - новый модуль, позволяющий получить массу полезной информации о выполняющейся программе на Python. Фактически, он предоставляет удобную обертку для разнообразного использования всевозможных "специальных" атрибутов объектов языка (func_*, co_*, im_*, tb_*, etc.), а также мощные средства интроспекции кода, описаний классов, etc.
pydoc - потрясающее средство оперативного просмотра и запросов к online-документации для Python. Наделяет интерактивный режим интерпретатора возможностями, сходными со справочными средствами Emacs, man и info. В состоянии извлекать документацию как из кода модулей, так и из файлов HTML. В качестве дополнительной возможности может быть запущен как сервер документации, позволяющий делать запросы через обычный Web-браузер, в том числе по сети; при этом браузер получает ответ в виде прекрасно форматированного HTML.
doctest и unittest - модули, предназначенные для поддержки тестирования других модулей. doctest обеспечивает оболочку для тестирования, выполняющую тесты, встроенные в __doc__ - строки функций, и сравнивающую реальный вывод с ожидаемым. unittest - много более мощный модуль, питоновская версия JUnit, оболочки для тестирования программ на Java (в свою очередь, являющийся Java-версией аналогичного пакета для Smalltalk). Поддерживает создание и прогон пакетов тестов. При этом пакеты тестов, в отличие от подхода doctest, являются отдельными сущностями.
difflib - предоставляет класс SequenceMatcher,
позволяющий вычислять разницу между
последовательностями, позволяющую
восстановить одну последовательность из
другой. Это позволяет писать программы
создания и наложения патчей. Кроме того,
используемый им алгоритм выдает результаты,
которые выглядят "правильно" с точки
зрения человеческого восприятия, что
позволяет писать качественные программы
построчного сравнения, наподобие diff. В
качестве примера см. Tools/scripts/ndiff.py
Новая версия PyXML. Поддерживает Expat версий 1.2 и последующих. Для версий Expat 1.95 и выше поддерживает дополнительные обработчики. Поддерживает разбор файлов во всех кодировках, поддерживаемых Python. Множество иправлений в модулях sax, minidom, pulldom.
В модуле zipfile объекты класса ZipFile могут теперь конструироваться не только из имени файла, но и из любого "файлоподобного" объекта.
Модуль random теперь самодостаточен и предоставляет всю функциональность, ранее предоставляемую модулем whrandom (который теперь считается устаревшим).
continue
может теперь присутствовать в
блоке try
, находящемся в теле цикла.
К словарю добавлен новый метод - popitem(). Он возвращает произвольный (недетерминированный) элемент словаря, удаляя его из этого словаря. Метод предназначен для разрушающей последовательной выборки значений словаря, если порядок выборки не имеет значения, и дает существенный выигрыш для больших словарей по сравнению с предварительным созданием списка элементов с последующей выборкой. Очень удобно для приложений, где словарь используется в качестве контейнера элементов, которые необходимо обработать все, неважно в каком порядке;
Значительно ускорен строчно-ориентированный ввод из текстовых файлов. Метод readline() стал работать приблизительно в 3 раза быстрее;
Добавлен новый файловый метод - xreadlines(). Он относится к readlines() так же, как xrange() к range(). Таким образом, самый быстрый, экономичный по памяти и элегантный метод построчного чтения текстового файла следующий:
for s in f.xreadlines():
do_somenthing(s)
Специальный метод __rcmp__ больше не поддерживается. Вместо него вызывается __cmp__ с инвертированным порядком операндов;
repr(), примененная к строке, в которой присутствуют не-ASCII и контрольные символы, использует для их вывода шетнадцатеричное, а не восьмеричное представление (наконец-то!);
Отменена поддержка Windows 3.1, DOS и OS/2 и удалены соответствующие плаформо-специфические каталоги из библиотеки.
[1]. Обратите внимание, что
конструкция import __future__
не является специальной - она просто
импортирует вполне реальный модуль __future__.py,
присутствующий в стандартной библиотеке
Python (в котором, правда, присутствует
определенная полезная информация об
опциональных свойствах).
[2]. Может возникнуть вопрос -
почему бы для поддержки семантики __future__
не
предусмотреть специальное ключевое слово
вместо своеобразной конструкции импорта?
Но проблема в том, что любое новое ключевое
слово - само по себе мощный источник
несовместимости; возникает порочный круг.
Кроме того,
[3]. PyErr_Warn() вызывает для обработки предупреждения код из модуля warnings, написанного на Python. Это следует иметь в виду, например, при создании "замороженных" приложений (т.е. монолитных файлов, где все необходимые модули на Python помещены в виде байт-кода в статические данные).
[4]. Это ограничение совершенно оправдано с точки зрения ясности кода. Поскольку атрибуты метода на самом деле устанавливаются для функции, его реализующей, разрешение присваивать атрибуты непосредственно через метод приводит к неочевидным результатам:
class C:
def a(self): pass
c1 = C()
c2 = C()
c1.a.publish = 1
# c2.a.publish теперь тоже равно 1 !!!
Причем установка атрибутов для несвязанного метода тоже может привести к неоднозначности:
class D(C): pass
class E(C): pass
D.a.publish = 1
# E.a.publish теперь тоже равно 1 !!!
[5]. Для типов, определнных в расширениях C, также определен механизм расширенного сравнения. Функция расширенного сравнения задается в слоте tp_richcompare и принимает дополнительноый третий параметр - целое число, сигнализирующее о том, какая именно операция сравнение вызвана.
[6]. Для тех, кто не знает - имена функций сравнения взяты из соответствующих операторов языка FORTRAN (EQ - EQual, равно; NE - Not Equal, не равно, etc.).
[7]. Если атрибут __dict__ функции
равен None
, первое присваивание любому (пользовательскому)
атрибуту функции автоматически создаст
словарь и поместит в него атрибут.