Что нового в Python 2.2¶
- Автор:
А.М. Кючлинг
Введение¶
В этой статье рассказывается о новых возможностях Python 2.2.2, выпущенного 14 октября 2002 года. Python 2.2.2 - это исправление ошибок в версии Python 2.2, первоначально выпущенной 21 декабря 2001 года.
Python 2.2 можно считать «чистым релизом». В нем есть несколько совершенно новых функций, таких как генераторы и итераторы, но большинство изменений, пусть и значительных и далеко идущих, направлены на устранение неровностей и темных углов в дизайне языка.
В этой статье мы не пытаемся дать полное описание новых возможностей, а предлагаем удобный обзор. За подробной информацией следует обращаться к документации по Python 2.2, например, Python Library Reference и Python Reference Manual. Если вы хотите понять полную реализацию и обоснование изменений, обратитесь к PEP для конкретной новой функции.
PEPs 252 и 253: Изменения типа и класса¶
Самые крупные и далеко идущие изменения в Python 2.2 касаются модели объектов и классов Python. Изменения должны быть обратно совместимы, так что, скорее всего, ваш код продолжит работать без изменений, но изменения предоставляют некоторые удивительные новые возможности. Прежде чем приступить к этому, самому длинному и сложному разделу статьи, я представлю обзор изменений и дам несколько комментариев.
A long time ago I wrote a web page listing flaws in Python’s design. One of the
most significant flaws was that it’s impossible to subclass Python types
implemented in C. In particular, it’s not possible to subclass built-in types,
so you can’t just subclass, say, lists in order to add a single useful method to
them. The UserList
module provides a class that supports all of the
methods of lists and that can be subclassed further, but there’s lots of C code
that expects a regular Python list and won’t accept a UserList
instance.
В Python 2.2 это исправлено, а также добавлено несколько новых интересных возможностей. Краткое резюме:
Вы можете создавать подклассы встроенных типов, таких как списки и даже целые числа, и ваши подклассы должны работать во всех местах, где требуется исходный тип.
Теперь можно определять статические методы и методы классов, в дополнение к методам экземпляров, доступным в предыдущих версиях Python.
It’s also possible to automatically call methods on accessing or setting an instance attribute by using a new mechanism called properties. Many uses of
__getattr__()
can be rewritten to use properties instead, making the resulting code simpler and faster. As a small side benefit, attributes can now have docstrings, too.Список допустимых атрибутов для экземпляра может быть ограничен определенным набором с помощью slots, что позволяет уберечься от опечаток и, возможно, сделать больше оптимизаций в будущих версиях Python.
Некоторые пользователи выразили беспокойство по поводу всех этих изменений. Конечно, говорят они, новые функции очень удобны и позволяют создавать всевозможные трюки, которые были невозможны в предыдущих версиях Python, но они также делают язык более сложным. Некоторые люди говорят, что всегда рекомендовали Python за его простоту, и чувствуют, что эта простота теряется.
Лично я считаю, что беспокоиться не стоит. Многие из новых возможностей довольно эзотеричны, и вы можете написать много кода на Python, не зная о них. Написать простой класс стало не сложнее, чем раньше, так что вам не стоит утруждать себя их изучением или обучением, если они действительно не нужны. Некоторые очень сложные задачи, которые раньше можно было решить только на C, теперь можно будет решить на чистом Python, и, на мой взгляд, это только к лучшему.
Я не буду пытаться описать все угловые случаи и мелкие изменения, которые потребовались для того, чтобы новые функции заработали. Вместо этого в этом разделе будут нарисованы лишь общие черты. Дополнительные источники информации о новой объектной модели Python 2.2 см. в разделе Похожие ссылки, «Связанные ссылки».
Старые и новые классы¶
Во-первых, вы должны знать, что в Python 2.2 действительно есть два вида классов: классические, или классы старого стиля, и классы нового стиля. Модель классов старого стиля точно такая же, как и модель классов в предыдущих версиях Python. Все новые возможности, описанные в этом разделе, применимы только к классам нового стиля. Это расхождение не должно продолжаться вечно; в конечном итоге классы старого стиля будут отменены, возможно, в Python 3.0.
Как же определить класс нового стиля? Вы делаете это путем подклассификации существующего класса нового стиля. Большинство встроенных типов Python, таких как целые числа, списки, словари и даже файлы, теперь являются классами нового стиля. Также был добавлен класс нового стиля object
, базовый класс для всех встроенных типов, так что если ни один встроенный тип не подходит, вы можете просто подклассифицировать object
:
class C(object):
def __init__ (self):
...
...
This means that class
statements that don’t have any base classes are
always classic classes in Python 2.2. (Actually you can also change this by
setting a module-level variable named __metaclass__
— see PEP 253
for the details — but it’s easier to just subclass object
.)
Объекты типов для встроенных типов доступны как встроенные объекты, названные с помощью хитрого трюка. В Python всегда были встроенные функции int()
, float()
и str()
. В версии 2.2 это уже не функции, а объекты типов, которые при вызове ведут себя как фабрики.
>>> int
<type 'int'>
>>> int('123')
123
To make the set of types complete, new type objects such as dict()
and
file()
have been added. Here’s a more interesting example, adding a
lock()
method to file objects:
class LockableFile(file):
def lock (self, operation, length=0, start=0, whence=0):
import fcntl
return fcntl.lockf(self.fileno(), operation,
length, start, whence)
The now-obsolete posixfile
module contained a class that emulated all of
a file object’s methods and also added a lock()
method, but this class
couldn’t be passed to internal functions that expected a built-in file,
something which is possible with our new LockableFile
.
Дескрипторы¶
In previous versions of Python, there was no consistent way to discover what
attributes and methods were supported by an object. There were some informal
conventions, such as defining __members__
and __methods__
attributes that were lists of names, but often the author of an extension type
or a class wouldn’t bother to define them. You could fall back on inspecting
the __dict__
of an object, but when class inheritance or an arbitrary
__getattr__()
hook were in use this could still be inaccurate.
Одна большая идея, лежащая в основе новой модели классов, заключается в том, что был формализован API для описания атрибутов объекта с помощью descriptors. Дескрипторы определяют значение атрибута, указывая, является ли он методом или полем. С помощью API дескрипторов становятся возможными статические методы и методы классов, а также более экзотические конструкции.
Дескрипторы атрибутов - это объекты, которые живут внутри объектов класса и имеют несколько собственных атрибутов:
__name__
- это имя атрибута.__doc__
is the attribute’s docstring.__get__(object)
- это метод, который извлекает значение атрибута из объекта.__set__(object, value)
устанавливает атрибут на объекте в значение.__delete__(object, value)
удаляет атрибут value из object.
Например, когда вы пишете obj.x
, Python на самом деле выполняет следующие действия:
descriptor = obj.__class__.x
descriptor.__get__(obj)
For methods, descriptor.__get__()
returns a temporary object that’s
callable, and wraps up the instance and the method to be called on it. This is
also why static methods and class methods are now possible; they have
descriptors that wrap up just the method, or the method and the class. As a
brief explanation of these new kinds of methods, static methods aren’t passed
the instance, and therefore resemble regular functions. Class methods are
passed the class of the object, but not the object itself. Static and class
methods are defined like this:
class C(object):
def f(arg1, arg2):
...
f = staticmethod(f)
def g(cls, arg1, arg2):
...
g = classmethod(g)
The staticmethod()
function takes the function f()
, and returns it
wrapped up in a descriptor so it can be stored in the class object. You might
expect there to be special syntax for creating such methods (def static f
,
defstatic f()
, or something like that) but no such syntax has been defined
yet; that’s been left for future versions of Python.
Более новые возможности, такие как слоты и свойства, также реализуются в виде новых видов дескрипторов, и написать класс дескриптора, который делает что-то новое, совсем несложно. Например, можно написать дескрипторный класс, позволяющий писать предварительные и последующие условия для метода в стиле Eiffel. Класс, использующий эту возможность, может быть определен следующим образом:
from eiffel import eiffelmethod
class C(object):
def f(self, arg1, arg2):
# The actual function
...
def pre_f(self):
# Check preconditions
...
def post_f(self):
# Check postconditions
...
f = eiffelmethod(f, pre_f, post_f)
Note that a person using the new eiffelmethod()
doesn’t have to understand
anything about descriptors. This is why I think the new features don’t increase
the basic complexity of the language. There will be a few wizards who need to
know about it in order to write eiffelmethod()
or the ZODB or whatever,
but most users will just write code on top of the resulting libraries and ignore
the implementation details.
Множественное наследование: Бриллиантовое правило¶
Множественное наследование также стало более полезным благодаря изменению правил, по которым разрешаются имена. Рассмотрим такой набор классов (диаграмма взята из PEP 253 Гвидо ван Россума):
class A:
^ ^ def save(self): ...
/ \
/ \
/ \
/ \
class B class C:
^ ^ def save(self): ...
\ /
\ /
\ /
\ /
class D
The lookup rule for classic classes is simple but not very smart; the base
classes are searched depth-first, going from left to right. A reference to
D.save()
will search the classes D
, B
, and then
A
, where save()
would be found and returned. C.save()
would never be found at all. This is bad, because if C
’s save()
method is saving some internal state specific to C
, not calling it will
result in that state never getting saved.
Классы нового стиля работают по другому алгоритму, который немного сложнее объяснить, но в данной ситуации он работает правильно. (Обратите внимание, что в Python 2.3 этот алгоритм изменен на тот, который дает те же результаты в большинстве случаев, но дает более полезные результаты для действительно сложных графов наследования).
List all the base classes, following the classic lookup rule and include a class multiple times if it’s visited repeatedly. In the above example, the list of visited classes is [
D
,B
,A
,C
,A
].Scan the list for duplicated classes. If any are found, remove all but one occurrence, leaving the last one in the list. In the above example, the list becomes [
D
,B
,C
,A
] after dropping duplicates.
Following this rule, referring to D.save()
will return C.save()
,
which is the behaviour we’re after. This lookup rule is the same as the one
followed by Common Lisp. A new built-in function, super()
, provides a way
to get at a class’s superclasses without having to reimplement Python’s
algorithm. The most commonly used form will be super(class, obj)
, which
returns a bound superclass object (not the actual class object). This form
will be used in methods to call a method in the superclass; for example,
D
’s save()
method would look like this:
class D (B,C):
def save (self):
# Call superclass .save()
super(D, self).save()
# Save D's private information here
...
super()
также может возвращать несвязанные объекты суперкласса, если вызвать его как super(class)
или super(class1, class2)
, но это, вероятно, не часто будет полезно.
Доступ к атрибутам¶
A fair number of sophisticated Python classes define hooks for attribute access
using __getattr__()
; most commonly this is done for convenience, to make
code more readable by automatically mapping an attribute access such as
obj.parent
into a method call such as obj.get_parent
. Python 2.2 adds
some new ways of controlling attribute access.
Во-первых, __getattr__(attr_name)
по-прежнему поддерживается классами нового стиля, и ничего в этом не изменилось. Как и раньше, он будет вызван, когда будет предпринята попытка доступа к obj.foo
и в словаре экземпляра не будет найден атрибут с именем foo
.
New-style classes also support a new method,
__getattribute__(attr_name)
. The difference between the two methods is
that __getattribute__()
is always called whenever any attribute is
accessed, while the old __getattr__()
is only called if foo
isn’t
found in the instance’s dictionary.
However, Python 2.2’s support for properties will often be a simpler way
to trap attribute references. Writing a __getattr__()
method is
complicated because to avoid recursion you can’t use regular attribute accesses
inside them, and instead have to mess around with the contents of
__dict__
. __getattr__()
methods also end up being called by Python
when it checks for other methods such as __repr__()
or __coerce__()
,
and so have to be written with this in mind. Finally, calling a function on
every attribute access results in a sizable performance loss.
property
is a new built-in type that packages up three functions that
get, set, or delete an attribute, and a docstring. For example, if you want to
define a size
attribute that’s computed, but also settable, you could
write:
class C(object):
def get_size (self):
result = ... computation ...
return result
def set_size (self, size):
... compute something based on the size
and set internal state appropriately ...
# Define a property. The 'delete this attribute'
# method is defined as None, so the attribute
# can't be deleted.
size = property(get_size, set_size,
None,
"Storage size of this instance")
That is certainly clearer and easier to write than a pair of
__getattr__()
/__setattr__()
methods that check for the size
attribute and handle it specially while retrieving all other attributes from the
instance’s __dict__
. Accesses to size
are also the only ones
which have to perform the work of calling a function, so references to other
attributes run at their usual speed.
Наконец, можно ограничить список атрибутов, на которые можно ссылаться в объекте, с помощью нового атрибута класса __slots__
. Объекты Python обычно очень динамичны; в любой момент можно определить новый атрибут для экземпляра, просто сделав obj.new_attr=1
. Класс нового стиля может определить атрибут класса с именем __slots__
, чтобы ограничить допустимые атрибуты определенным набором имен. Это станет ясно из примера:
>>> class C(object):
... __slots__ = ('template', 'name')
...
>>> obj = C()
>>> print obj.template
None
>>> obj.template = 'Test'
>>> print obj.template
Test
>>> obj.newattr = None
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'C' object has no attribute 'newattr'
Обратите внимание, что при попытке присвоить атрибут, не указанный в __slots__
, вы получаете AttributeError
.
PEP 234: Итераторы¶
Еще одно существенное дополнение к 2.2 - интерфейс итерации на уровне C и Python. Объекты могут определять, как их могут обходить вызывающие стороны.
В версиях Python до 2.1 обычный способ заставить for item in obj
работать - это определить метод __getitem__()
, который выглядит примерно так:
def __getitem__(self, index):
return <next item>
__getitem__()
правильнее использовать для определения операции индексирования объекта, чтобы вы могли написать obj[5]
для получения шестого элемента. Это немного вводит в заблуждение, когда вы используете его только для поддержки циклов for
. Рассмотрим какой-нибудь файлоподобный объект, по которому нужно выполнить цикл; параметр index, по сути, не имеет смысла, поскольку класс, вероятно, предполагает, что будет выполнена серия вызовов __getitem__()
с index, увеличивающимся на единицу каждый раз. Другими словами, наличие метода __getitem__()
не означает, что использование file[5]
для произвольного доступа к шестому элементу будет работать, хотя на самом деле должно.
В Python 2.2 итерация может быть реализована отдельно, а методы __getitem__()
могут быть ограничены классами, которые действительно поддерживают произвольный доступ. Основная идея итераторов проста. Для получения итератора используется новая встроенная функция iter(obj)
или iter(C, sentinel)
. iter(obj)
возвращает итератор для объекта obj, а iter(C, sentinel)
возвращает итератор, который будет вызывать вызываемый объект C, пока не вернет sentinel, сигнализирующий о том, что итератор завершен.
Python classes can define an __iter__()
method, which should create and
return a new iterator for the object; if the object is its own iterator, this
method can just return self
. In particular, iterators will usually be their
own iterators. Extension types implemented in C can implement a tp_iter
function in order to return an iterator, and extension types that want to behave
as iterators can define a tp_iternext
function.
Итак, что же на самом деле делают итераторы после всего этого? У них есть один обязательный метод next()
, который не принимает никаких аргументов и возвращает следующее значение. Когда больше нет значений для возврата, вызов next()
должен вызвать исключение StopIteration
.
>>> L = [1,2,3]
>>> i = iter(L)
>>> print i
<iterator object at 0x8116870>
>>> i.next()
1
>>> i.next()
2
>>> i.next()
3
>>> i.next()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
StopIteration
>>>
In 2.2, Python’s for
statement no longer expects a sequence; it
expects something for which iter()
will return an iterator. For backward
compatibility and convenience, an iterator is automatically constructed for
sequences that don’t implement __iter__()
or a tp_iter
slot, so
for i in [1,2,3]
will still work. Wherever the Python interpreter loops
over a sequence, it’s been changed to use the iterator protocol. This means you
can do things like this:
>>> L = [1,2,3]
>>> i = iter(L)
>>> a,b,c = i
>>> a,b,c
(1, 2, 3)
В некоторые базовые типы Python была добавлена поддержка итераторов. Вызов iter()
на словаре вернет итератор, который циклически перебирает его ключи:
>>> m = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6,
... 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12}
>>> for key in m: print key, m[key]
...
Mar 3
Feb 2
Aug 8
Sep 9
May 5
Jun 6
Jul 7
Jan 1
Apr 4
Nov 11
Dec 12
Oct 10
That’s just the default behaviour. If you want to iterate over keys, values, or
key/value pairs, you can explicitly call the iterkeys()
,
itervalues()
, or iteritems()
methods to get an appropriate iterator.
In a minor related change, the in
operator now works on dictionaries,
so key in dict
is now equivalent to dict.has_key(key)
.
Файлы также предоставляют итератор, который вызывает метод readline()
до тех пор, пока в файле не останется ни одной строки. Это означает, что теперь вы можете читать каждую строку файла, используя код, подобный этому:
for line in file:
# do something for each line
...
Обратите внимание, что в итераторе можно двигаться только вперед; нет способа получить предыдущий элемент, сбросить итератор или создать его копию. Объект итератора мог бы предоставить такие дополнительные возможности, но протокол итератора требует только метода next()
.
См.также
- PEP 234 - Итераторы
Написано Ka-Ping Yee и GvR; реализовано командой Python Labs, в основном GvR и Тимом Питерсом.
PEP 255: Простые генераторы¶
Генераторы - это еще одна новая возможность, которая взаимодействует с введением итераторов.
Вы, несомненно, знакомы с тем, как работают вызовы функций в Python или C. Когда вы вызываете функцию, она получает приватное пространство имен, где создаются ее локальные переменные. Когда функция достигает оператора return
, локальные переменные уничтожаются, а результирующее значение возвращается вызывающему. При последующем вызове той же функции будет получен новый набор локальных переменных. Но что, если бы локальные переменные не выбрасывались при выходе из функции? Что если бы вы могли позже возобновить работу функции с того места, на котором она остановилась? Именно это и обеспечивают генераторы; их можно рассматривать как функции с возможностью возобновления.
Вот простейший пример функции-генератора:
def generate_ints(N):
for i in range(N):
yield i
Для генераторов было введено новое ключевое слово yield
. Любая функция, содержащая оператор yield
, является функцией-генератором; это обнаруживает компилятор байткода Python, который в результате компилирует функцию особым образом. Поскольку было введено новое ключевое слово, генераторы должны быть явно включены в модуль путем включения утверждения from __future__ import generators
в верхней части исходного кода модуля. В Python 2.3 это утверждение станет ненужным.
Когда вы вызываете функцию-генератор, она не возвращает одно значение, вместо этого она возвращает объект-генератор, который поддерживает протокол итератора. При выполнении оператора yield
генератор выводит значение i
, аналогично оператору return
. Существенное отличие оператора yield
от оператора return
заключается в том, что при достижении оператора yield
состояние выполнения генератора приостанавливается, а локальные переменные сохраняются. При следующем вызове метода генератора next()
функция возобновит выполнение сразу после оператора yield
. (По сложным причинам оператор yield
не допускается внутри try
блока оператора try
…finally
; читайте PEP 255 для полного объяснения взаимодействия между yield
и исключениями).
Here’s a sample usage of the generate_ints()
generator:
>>> gen = generate_ints(3)
>>> gen
<generator object at 0x8117f90>
>>> gen.next()
0
>>> gen.next()
1
>>> gen.next()
2
>>> gen.next()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 2, in generate_ints
StopIteration
С тем же успехом можно написать for i in generate_ints(5)
или a,b,c = generate_ints(3)
.
Внутри функции-генератора оператор return
может использоваться только без значения и сигнализирует о завершении процесса получения значений; после этого генератор не может возвращать больше никаких значений. return
со значением, например return 5
, является синтаксической ошибкой внутри функции-генератора. Конец результатов работы генератора также можно обозначить, подняв вручную StopIteration
или просто позволив потоку выполнения упасть в нижнюю часть функции.
Вы можете добиться эффекта генераторов вручную, написав свой собственный класс и сохранив все локальные переменные генератора как переменные экземпляра. Например, возврат списка целых чисел можно сделать, установив self.count
в 0, а метод next()
увеличит self.count
и вернет его. Однако для умеренно сложного генератора написать соответствующий класс будет гораздо сложнее. Lib/test/test_generators.py
содержит ряд более интересных примеров. Самый простой из них реализует последовательный обход дерева с рекурсивным использованием генераторов.
# A recursive generator that generates Tree leaves in in-order.
def inorder(t):
if t:
for x in inorder(t.left):
yield x
yield t.label
for x in inorder(t.right):
yield x
Два других примера в Lib/test/test_generators.py
дают решения для задачи N-Queens (размещение $N$ ферзей на шахматной доске $NxN$ так, чтобы ни один ферзь не угрожал другому) и Knight’s Tour (маршрут, по которому конь попадает на каждую клетку шахматной доски $NxN$, не посещая ни одной клетки дважды).
Идея генераторов пришла из других языков программирования, особенно из Icon (https://www2.cs.arizona.edu/icon/), где идея генераторов занимает центральное место. В Icon каждое выражение и вызов функции ведут себя как генератор. Один пример из «Обзора языка программирования Icon» на https://www2.cs.arizona.edu/icon/docs/ipd266.htm дает представление о том, как это выглядит:
sentence := "Store it in the neighboring harbor"
if (i := find("or", sentence)) > 5 then write(i)
In Icon the find()
function returns the indexes at which the substring
«or» is found: 3, 23, 33. In the if
statement, i
is first
assigned a value of 3, but 3 is less than 5, so the comparison fails, and Icon
retries it with the second value of 23. 23 is greater than 5, so the comparison
now succeeds, and the code prints the value 23 to the screen.
Python не заходит так далеко, как Icon, в принятии генераторов в качестве центральной концепции. Генераторы считаются новой частью основного языка Python, но их изучение или использование не является обязательным; если они не решают никаких проблем, которые у вас есть, можете не обращать на них внимания. Одна из новых особенностей интерфейса Python по сравнению с интерфейсом Icon заключается в том, что состояние генератора представлено в виде конкретного объекта (итератора), который можно передавать другим функциям или хранить в структуре данных.
См.также
- PEP 255 - Простые генераторы
Авторы сценария: Нил Шеменауэр, Тим Питерс, Магнус Ли Хетланд. Реализовано в основном Нилом Шеменауэром и Тимом Питерсом, другие исправления внесены командой Python Labs.
PEP 237: Унификация длинных целых и целых чисел¶
In recent versions, the distinction between regular integers, which are 32-bit
values on most machines, and long integers, which can be of arbitrary size, was
becoming an annoyance. For example, on platforms that support files larger than
2**32
bytes, the tell()
method of file objects has to return a long
integer. However, there were various bits of Python that expected plain integers
and would raise an error if a long integer was provided instead. For example,
in Python 1.5, only regular integers could be used as a slice index, and
'abc'[1L:]
would raise a TypeError
exception with the message „slice
index must be int“.
В Python 2.2 значения из коротких целых чисел в длинные будут переводиться по мере необходимости. Суффикс „L“ больше не нужен для обозначения длинного целочисленного литерала, так как теперь компилятор сам выберет соответствующий тип. (Использование суффикса „L“ не будет поощряться в последующих версиях Python 2.x, вызывая предупреждение в Python 2.4, и, вероятно, будет отменено в Python 3.0). Многие операции, которые раньше выдавали OverflowError
, теперь будут возвращать длинное целое число в качестве результата. Например:
>>> 1234567890123
1234567890123L
>>> 2 ** 64
18446744073709551616L
В большинстве случаев целые и длинные целые числа теперь будут обрабатываться одинаково. Вы все еще можете различать их с помощью встроенной функции type()
, но это редко требуется.
См.также
- PEP 237 - Унификация длинных целых и целых чисел
Авторы: Моше Задка и Гвидо ван Россум. Реализовано в основном Гвидо ван Россумом.
PEP 238: Изменение оператора деления¶
Самое спорное изменение в Python 2.2 предвещает начало работы по исправлению старого недостатка, который был в Python с самого начала. В настоящее время оператор деления в Python, /
, ведет себя так же, как оператор деления в C, когда ему предъявляются два целочисленных аргумента: он возвращает целочисленный результат, который усекается, если в нем есть дробная часть. Например, 3/2
- это 1, а не 1,5, а (-1)/2
- это -1, а не -0,5. Это означает, что результаты деления могут неожиданно меняться в зависимости от типа двух операндов, а поскольку Python имеет динамическую типизацию, определить возможные типы операндов может быть непросто.
(Спор идет о том, действительно ли это недостаток дизайна, и стоит ли ломать существующий код, чтобы его исправить. Это вызвало бесконечные дискуссии на python-dev, а в июле 2001 года вылилось в бурю кислотно-саркастических постов на comp.lang.python. Здесь я не буду отстаивать ни одну из сторон и ограничусь описанием того, что реализовано в 2.2. Читайте PEP 238 для краткого изложения аргументов и контраргументов).
Поскольку это изменение может сломать код, оно вводится очень постепенно. Python 2.2 начинает переход, но полностью переход будет завершен только в Python 3.0.
Во-первых, я позаимствую некоторую терминологию у PEP 238. «Истинное деление» - это деление, с которым знакомо большинство непрограммистов: 3/2 - это 1,5, 1/4 - 0,25 и так далее. «Деление с полом» - это то, что в настоящее время делает оператор Python /
, когда ему передаются целочисленные операнды; результатом является пол значения, возвращаемого истинным делением. «Классическое деление» - это текущее смешанное поведение оператора /
; он возвращает результат деления на пол, когда операндами являются целые числа, и возвращает результат истинного деления, когда одним из операндов является число с плавающей точкой.
Вот какие изменения вносит 2.2:
Новый оператор
//
- это оператор деления на пол. (Да, мы знаем, что он похож на символ комментария в C++.)//
всегда выполняет деление на пол, независимо от типов его операндов, поэтому1 // 2
равен 0 и1.0 // 2.0
также равен 0.0.//
всегда доступен в Python 2.2; вам не нужно включать его с помощью оператора__future__
.Если включить в модуль оператор
from __future__ import division
, то оператор/
будет заменен на возврат результата истинного деления, так что1/2
будет равен 0,5. Без оператора__future__
оператор/
по-прежнему означает классическое деление. Значение оператора/
по умолчанию не изменится до Python 3.0.Classes can define methods called
__truediv__()
and__floordiv__()
to overload the two division operators. At the C level, there are also slots in thePyNumberMethods
structure so extension types can define the two operators.Python 2.2 поддерживает некоторые аргументы командной строки для проверки того, будет ли код работать с измененной семантикой деления. Запуск python с аргументом
-Q warn
приведет к выдаче предупреждения всякий раз, когда деление применяется к двум целым числам. Вы можете использовать это, чтобы найти код, на который повлияло изменение, и исправить его. По умолчанию Python 2.2 будет просто выполнять классическое деление без предупреждения; в Python 2.3 предупреждение будет включено по умолчанию.
См.также
- PEP 238 - Изменение оператора деления
Авторы: Моше Задка и Гвидо ван Россум. Реализовано Гвидо ван Россумом.
Изменения в Юникоде¶
В версии 2.2 поддержка Юникода в Python была немного расширена. Строки Юникода обычно хранятся в UCS-2, как 16-битные целые числа без знака. Python 2.2 также можно скомпилировать для использования UCS-4, 32-битных целых без знака, в качестве внутренней кодировки, указав --enable-unicode=ucs4
в скрипте configure. (Также можно указать --disable-unicode
, чтобы полностью отключить поддержку Юникода).
When built to use UCS-4 (a «wide Python»), the interpreter can natively handle
Unicode characters from U+000000 to U+110000, so the range of legal values for
the unichr()
function is expanded accordingly. Using an interpreter
compiled to use UCS-2 (a «narrow Python»), values greater than 65535 will still
cause unichr()
to raise a ValueError
exception. This is all
described in PEP 261, «Support for „wide“ Unicode characters»; consult it for
further details.
Another change is simpler to explain. Since their introduction, Unicode strings
have supported an encode()
method to convert the string to a selected
encoding such as UTF-8 or Latin-1. A symmetric decode([*encoding*])
method has been added to 8-bit strings (though not to Unicode strings) in 2.2.
decode()
assumes that the string is in the specified encoding and decodes
it, returning whatever is returned by the codec.
С помощью этой новой функции были добавлены кодеки для задач, не связанных напрямую с Unicode. Например, были добавлены кодеки для uu-кодирования, кодирования MIME base64 и сжатия с помощью модуля zlib
:
>>> s = """Here is a lengthy piece of redundant, overly verbose,
... and repetitive text.
... """
>>> data = s.encode('zlib')
>>> data
'x\x9c\r\xc9\xc1\r\x80 \x10\x04\xc0?Ul...'
>>> data.decode('zlib')
'Here is a lengthy piece of redundant, overly verbose,\nand repetitive text.\n'
>>> print s.encode('uu')
begin 666 <data>
M2&5R92!I<R!A(&QE;F=T:'D@<&EE8V4@;V8@<F5D=6YD86YT+"!O=F5R;'D@
>=F5R8F]S92P*86YD(')E<&5T:71I=F4@=&5X="X*
end
>>> "sheesh".encode('rot-13')
'furrfu'
To convert a class instance to Unicode, a __unicode__()
method can be
defined by a class, analogous to __str__()
.
encode()
, decode()
, and __unicode__()
were implemented by
Marc-André Lemburg. The changes to support using UCS-4 internally were
implemented by Fredrik Lundh and Martin von Löwis.
См.также
- PEP 261 - Поддержка «широких» символов Unicode
Автор Пол Прескод.
PEP 227: Вложенные области¶
В Python 2.1 статически вложенные диапазоны были добавлены в качестве опциональной возможности, которую можно было включить директивой from __future__ import nested_scopes
. В версии 2.2 вложенные области больше не нужно специально включать, теперь они присутствуют всегда. Остальная часть этого раздела представляет собой копию описания вложенных диапазонов из моего документа «Что нового в Python 2.1»; если вы читали его, когда вышла 2.1, то можете пропустить остальную часть этого раздела.
Самое крупное изменение, внесенное в Python 2.1 и дополненное в 2.2, касается правил поиска переменных в Python. В Python 2.0 в любой момент времени для поиска имен переменных используется не более трех пространств имен: локальное, на уровне модуля и встроенное пространство имен. Это часто удивляло людей, поскольку не соответствовало их интуитивным ожиданиям. Например, определение вложенной рекурсивной функции не работает:
def f():
...
def g(value):
...
return g(value-1) + 1
...
The function g()
will always raise a NameError
exception, because
the binding of the name g
isn’t in either its local namespace or in the
module-level namespace. This isn’t much of a problem in practice (how often do
you recursively define interior functions like this?), but this also made using
the lambda
expression clumsier, and this was a problem in practice.
In code which uses lambda
you can often find local variables being
copied by passing them as the default values of arguments.
def find(self, name):
"Return list of any entries equal to 'name'"
L = filter(lambda x, name=name: x == name,
self.list_attribute)
return L
В результате сильно страдает читаемость кода на Python, написанного в ярко выраженном функциональном стиле.
Самым значительным изменением в Python 2.2 стало добавление в язык статического скопирования для решения этой проблемы. В качестве первого эффекта, аргумент по умолчанию name=name
теперь не нужен в приведенном выше примере. Проще говоря, когда заданному имени переменной не присваивается значение внутри функции (присваиванием или операторами def
, class
или import
), ссылки на переменную будут искаться в локальном пространстве имен объемлющей области видимости. Более подробное объяснение правил и разбор реализации можно найти в PEP.
Это изменение может вызвать некоторые проблемы совместимости для кода, в котором одно и то же имя переменной используется как на уровне модуля, так и в качестве локальной переменной в функции, содержащей дальнейшие определения функции. Однако это кажется довольно маловероятным, поскольку такой код изначально был бы довольно запутанным для чтения.
Одним из побочных эффектов этого изменения является то, что операторы from module import *
и exec
стали незаконными внутри области видимости функции при определенных условиях. В справочном руководстве по Python все время говорилось, что from module import *
легальны только на верхнем уровне модуля, но интерпретатор CPython никогда раньше этого не делал. В рамках реализации вложенных областей компилятор, превращающий исходный текст Python в байткод, должен генерировать различный код для доступа к переменным в содержащей области видимости. from module import *
и exec
не позволяют компилятору понять это, поскольку добавляют в локальное пространство имен имена, которые неизвестны во время компиляции. Поэтому, если функция содержит определения функций или lambda
выражения со свободными переменными, компилятор отметит это, вызвав SyntaxError
исключение.
Чтобы сделать предыдущее объяснение немного понятнее, приведем пример:
x = 1
def f():
# The next line is a syntax error
exec 'x=2'
def g():
return x
Line 4 containing the exec
statement is a syntax error, since
exec
would define a new local variable named x
whose value should
be accessed by g()
.
Это не должно быть большим ограничением, так как exec
редко используется в большинстве кода Python (а когда используется, это часто является признаком плохого дизайна в любом случае).
См.также
- PEP 227 - Статически вложенные области
Автор и исполнитель Джереми Хилтон.
Новые и улучшенные модули¶
The
xmlrpclib
module was contributed to the standard library by Fredrik Lundh, providing support for writing XML-RPC clients. XML-RPC is a simple remote procedure call protocol built on top of HTTP and XML. For example, the following snippet retrieves a list of RSS channels from the O’Reilly Network, and then lists the recent headlines for one channel:import xmlrpclib s = xmlrpclib.Server( 'http://www.oreillynet.com/meerkat/xml-rpc/server.php') channels = s.meerkat.getChannels() # channels is a list of dictionaries, like this: # [{'id': 4, 'title': 'Freshmeat Daily News'} # {'id': 190, 'title': '32Bits Online'}, # {'id': 4549, 'title': '3DGamers'}, ... ] # Get the items for one channel items = s.meerkat.getItems( {'channel': 4} ) # 'items' is another list of dictionaries, like this: # [{'link': 'http://freshmeat.net/releases/52719/', # 'description': 'A utility which converts HTML to XSL FO.', # 'title': 'html2fo 0.3 (Default)'}, ... ]
The
SimpleXMLRPCServer
module makes it easy to create straightforward XML-RPC servers. See http://xmlrpc.scripting.com/ for more information about XML-RPC.Новый модуль
hmac
реализует алгоритм HMAC, описанный в RFC 2104. (Внесен Герхардом Херингом).Several functions that originally returned lengthy tuples now return pseudo-sequences that still behave like tuples but also have mnemonic attributes such as
memberst_mtime
ortm_year
. The enhanced functions includestat()
,fstat()
,statvfs()
, andfstatvfs()
in theos
module, andlocaltime()
,gmtime()
, andstrptime()
in thetime
module.Например, чтобы получить размер файла с помощью старых кортежей, вы должны были написать что-то вроде
file_size = os.stat(filename)[stat.ST_SIZE]
, но теперь это можно записать более четко какfile_size = os.stat(filename).st_size
.Оригинальный патч для этой функции был предоставлен Ником Мэтьюсоном.
Профилировщик Python был значительно переработан, исправлены различные ошибки в его выводе. (Вклад внесли Фред Л. Дрейк-младший и Тим Питерс).
Модуль
socket
может быть скомпилирован для поддержки IPv6; укажите опцию--enable-ipv6
в скрипте configure Python. (Внесено Дзюн-итиро «itojun» Хагино).В модуль
struct
были добавлены два новых символа формата для 64-битных целых чисел на платформах, поддерживающих тип C long long.q
предназначен для знакового 64-битного целого числа, аQ
- для беззнакового. Значение возвращается в типе long integer в Python. (Внесено Тимом Питерсом.)В интерактивном режиме интерпретатора появилась новая встроенная функция
help()
, которая использует модульpydoc
, представленный в Python 2.1, для предоставления интерактивной справки.help(object)
выводит любой доступный текст справки об объекте.help()
без аргумента переводит вас в интерактивную справочную утилиту, где вы можете ввести имена функций, классов или модулей, чтобы прочитать их справочный текст. (Внесено Гвидо ван Россумом, с использованием модуляpydoc
Ка-Пин Йи).Various bugfixes and performance improvements have been made to the SRE engine underlying the
re
module. For example, there.sub()
andre.split()
functions have been rewritten in C. Another contributed patch speeds up certain Unicode character ranges by a factor of two, and a newfinditer()
method that returns an iterator over all the non-overlapping matches in a given string. (SRE is maintained by Fredrik Lundh. The BIGCHARSET patch was contributed by Martin von Löwis.)Модуль
smtplib
теперь поддерживает RFC 2487, «Secure SMTP over TLS», так что теперь можно шифровать SMTP-трафик между программой на Python и почтовым транспортным агентом, которому передается сообщение.smtplib
также поддерживает SMTP-аутентификацию. (Внесено Герхардом Херингом).Модуль
imaplib
, поддерживаемый Пирсом Лаудером, поддерживает несколько новых расширений: расширение NAMESPACE, определенное в RFC 2342, SORT, GETACL и SETACL. (Вклад Энтони Бакстера и Мишеля Пеллетье).The
rfc822
module’s parsing of email addresses is now compliant with RFC 2822, an update to RFC 822. (The module’s name is not going to be changed torfc2822
.) A new package,email
, has also been added for parsing and generating e-mail messages. (Contributed by Barry Warsaw, and arising out of his work on Mailman.)The
difflib
module now contains a newDiffer
class for producing human-readable lists of changes (a «delta») between two sequences of lines of text. There are also two generator functions,ndiff()
andrestore()
, which respectively return a delta from two sequences, or one of the original sequences from a delta. (Grunt work contributed by David Goodger, from ndiff.py code by Tim Peters who then did the generatorization.)New constants
ascii_letters
,ascii_lowercase
, andascii_uppercase
were added to thestring
module. There were several modules in the standard library that usedstring.letters
to mean the ranges A-Za-z, but that assumption is incorrect when locales are in use, becausestring.letters
varies depending on the set of legal characters defined by the current locale. The buggy modules have all been fixed to useascii_letters
instead. (Reported by an unknown person; fixed by Fred L. Drake, Jr.)The
mimetypes
module now makes it easier to use alternative MIME-type databases by the addition of aMimeTypes
class, which takes a list of filenames to be parsed. (Contributed by Fred L. Drake, Jr.)A
Timer
class was added to thethreading
module that allows scheduling an activity to happen at some future time. (Contributed by Itamar Shtull-Trauring.)
Изменения и исправления в интерпретаторе¶
Некоторые изменения касаются только тех, кто работает с интерпретатором Python на уровне C, поскольку они пишут модули расширения Python, встраивают интерпретатор или просто взламывают сам интерпретатор. Если вы пишете только код на Python, то ни одно из описанных здесь изменений вас сильно не затронет.
Функции профилирования и трассировки теперь могут быть реализованы на языке C, который может работать на гораздо более высоких скоростях, чем функции на Python, и должен снизить накладные расходы на профилирование и трассировку. Это будет интересно авторам сред разработки для Python. В API Python были добавлены две новые функции на языке C,
PyEval_SetProfile()
иPyEval_SetTrace()
. Существующие функцииsys.setprofile()
иsys.settrace()
по-прежнему существуют, они просто были изменены для использования нового интерфейса на уровне C. (Внесено Фредом Л. Дрейком-младшим)Был добавлен еще один низкоуровневый API, который в первую очередь будет интересен разработчикам отладчиков и инструментов разработки на Python.
PyInterpreterState_Head()
иPyInterpreterState_Next()
позволяют вызывающей стороне пройтись по всем существующим объектам интерпретатора;PyInterpreterState_ThreadHead()
иPyThreadState_Next()
позволяют перебрать все состояния потоков для данного интерпретатора. (Внесено Дэвидом Бизли).Интерфейс сборщика мусора на уровне языка C был изменен, чтобы упростить написание типов расширения, поддерживающих сборку мусора, и отладку неправильного использования функций. Различные функции имеют немного разную семантику, поэтому кучу функций пришлось переименовать. Расширения, использующие старый API, по-прежнему будут компилироваться, но не будут участвовать в сборке мусора, поэтому их обновление для 2.2 следует считать достаточно приоритетным.
Чтобы обновить модуль расширения до нового API, выполните следующие действия:
Переименуйте
Py_TPFLAGS_GC
вPy_TPFLAGS_HAVE_GC
.- Используйте
PyObject_GC_New()
илиPyObject_GC_NewVar()
для выделения объектов, а
PyObject_GC_Del()
- для их деаллокации.
- Используйте
Переименуйте
PyObject_GC_Init()
вPyObject_GC_Track()
иPyObject_GC_Fini()
вPyObject_GC_UnTrack()
.Уберите
PyGC_HEAD_SIZE
из расчетов размера объекта.Удалите вызовы
PyObject_AS_GC()
иPyObject_FROM_GC()
.В
PyArg_ParseTuple()
добавлена новая последовательность форматаet
;et
принимает параметр и имя кодировки и преобразует параметр в заданную кодировку, если параметр оказывается строкой Unicode, или оставляет его в покое, если это 8-битная строка, предполагая, что она уже находится в нужной кодировке. Это отличается от символа форматаes
, который предполагает, что 8-битные строки находятся в кодировке ASCII, принятой в Python по умолчанию, и преобразует их в указанную новую кодировку. (Предоставлено M.-A. Лембургом и используется для поддержки MBCS в Windows, описанной в следующем разделе).Добавлена другая функция разбора аргументов,
PyArg_UnpackTuple()
, более простая и, предположительно, более быстрая. Вместо указания строки формата вызывающая сторона просто указывает минимальное и максимальное количество ожидаемых аргументов, а также набор указателей на переменные PyObject*, которые будут заполнены значениями аргументов.Два новых флага
METH_NOARGS
иMETH_O
доступны в таблицах определений методов для упрощения реализации методов без аргументов или с одним нетипизированным аргументом. Вызов таких методов более эффективен, чем вызов соответствующего метода, использующегоMETH_VARARGS
. Кроме того, старый стильMETH_OLDARGS
для написания методов на языке C теперь официально устарел.Two new wrapper functions,
PyOS_snprintf()
andPyOS_vsnprintf()
were added to provide cross-platform implementations for the relatively newsnprintf()
andvsnprintf()
C lib APIs. In contrast to the standardsprintf()
andvsprintf()
functions, the Python versions check the bounds of the buffer used to protect against buffer overruns. (Contributed by M.-A. Lemburg.)Функция
_PyTuple_Resize()
потеряла неиспользуемый параметр, теперь она принимает 2 параметра вместо 3. Третий аргумент никогда не использовался и может быть просто отброшен при переносе кода с предыдущих версий на Python 2.2.
Другие изменения и исправления¶
Как обычно, по всему дереву исходников разбросано множество других улучшений и исправлений. Поиск по журналам изменений CVS показывает, что между Python 2.1 и 2.2 было применено 527 исправлений и исправлено 683 ошибки; в 2.2.1 было применено 139 исправлений и исправлено 143 ошибки; в 2.2.2 было применено 106 исправлений и исправлено 82 ошибки. Эти цифры, скорее всего, являются заниженными.
Вот некоторые из наиболее заметных изменений:
Код порта Python для MacOS, поддерживаемый Джеком Янсеном, теперь хранится в основном CVS-дереве Python, и в него было внесено множество изменений для поддержки MacOS X.
Самым значительным изменением является возможность собирать Python как фреймворк, что можно сделать, добавив опцию
--enable-framework
в скрипт configure при компиляции Python. По словам Джека Янсена, «это устанавливает автономную установку Python плюс «клей» фреймворка OS X в/Library/Frameworks/Python.framework
(или другое место по выбору). На данный момент от этого мало пользы (на самом деле, есть недостаток - вам придется изменить PATH, чтобы найти Python), но это основа для создания полноценного Python-приложения, переноса MacPython IDE, возможного использования Python в качестве стандартного языка сценариев OSA и многого другого».Большинство модулей инструментария MacPython, которые взаимодействуют с API MacOS, такими как оконный режим, QuickTime, скриптинг и т.д., были перенесены на OS X, но они были оставлены закомментированными в
setup.py
. Люди, которые хотят поэкспериментировать с этими модулями, могут откомментировать их вручную.Аргументы ключевых слов, передаваемые встроенным функциям, которые их не принимают, теперь вызывают исключение
TypeError
с сообщением «функция не принимает аргументов ключевых слов».Слабые ссылки, добавленные в Python 2.1 в качестве модуля расширения, теперь являются частью ядра, поскольку используются в реализации классов нового стиля. Поэтому исключение
ReferenceError
переехало из модуляweakref
и стало встроенным исключением.Новый скрипт
Tools/scripts/cleanfuture.py
от Тима Питерса автоматически удаляет устаревшие утверждения__future__
из исходного кода Python.Во встроенную функцию
compile()
был добавлен дополнительный аргумент flags, так что теперь поведение операторов__future__
можно корректно наблюдать в симулированных оболочках, таких как IDLE и другие среды разработки. Это описано в разделе PEP 264. (Внесено Майклом Хадсоном.)Новая лицензия, появившаяся в Python 1.6, была несовместима с GPL. Это исправлено небольшими текстовыми изменениями в лицензии 2.2, так что теперь встраивание Python в программы под GPL снова законно. Обратите внимание, что сам Python не находится под GPL, а работает под лицензией, которая по сути эквивалентна лицензии BSD, как это было всегда. Изменения в лицензии были также применены к выпускам Python 2.0.1 и 2.1.1.
При представлении имени файла в формате Unicode в Windows Python теперь преобразует его в строку в кодировке MBCS, которая используется в файловых API Microsoft. Поскольку MBCS явно используется файловыми API, выбор Python ASCII в качестве кодировки по умолчанию оказывается раздражающим фактором. На Unix используется набор символов локали, если доступно
locale.nl_langinfo(CODESET)
. (Поддержка Windows была осуществлена Марком Хэммондом при содействии Марка-Андре Лембурга. Поддержка Unix была добавлена Мартином фон Лёвисом).Поддержка больших файлов теперь включена в Windows. (Внесено Тимом Питерсом).
Скрипт
Tools/scripts/ftpmirror.py
теперь разбирает файл.netrc
, если он у вас есть. (Внесено Майком Ромбергом).Some features of the object returned by the
xrange()
function are now deprecated, and trigger warnings when they’re accessed; they’ll disappear in Python 2.3.xrange
objects tried to pretend they were full sequence types by supporting slicing, sequence multiplication, and thein
operator, but these features were rarely used and therefore buggy. Thetolist()
method and thestart
,stop
, andstep
attributes are also being deprecated. At the C level, the fourth argument to thePyRange_New()
function,repeat
, has also been deprecated.Было внесено множество исправлений в реализацию словарей, в основном для исправления потенциальных дампов ядра, если словарь содержал объекты, которые незаметно меняли свое хэш-значение или мутировали словарь, в котором они содержались. На некоторое время python-dev вошел в мягкий ритм: Майкл Хадсон находил случай, который приводил к дампу ядра, Тим Питерс исправлял ошибку, Майкл находил другой случай, и так по кругу.
В Windows Python теперь можно компилировать с Borland C благодаря ряду исправлений, внесенных Стивеном Хансеном, хотя результат еще не полностью функционален. (Но это уже прогресс…)
Еще одно усовершенствование Windows: Компания Wise Solutions щедро предложила PythonLabs использовать свою систему InstallerMaster 8.1. Предыдущие программы установки PythonLabs под Windows использовали Wise 5.0a, которая уже начала показывать свой возраст. (Упаковано Тимом Питерсом.)
Файлы, заканчивающиеся на
.pyw
, теперь можно импортировать в Windows..pyw
- это особенность только для Windows, используемая для обозначения того, что скрипт должен быть запущен с помощью PYTHONW.EXE, а не PYTHON.EXE, чтобы предотвратить появление DOS-консоли для отображения вывода. Этот патч позволяет импортировать такие скрипты, если они также могут быть использованы в качестве модулей. (Реализовано Дэвидом Боленом).На платформах, где Python использует функцию C
dlopen()
для загрузки модулей расширения, теперь можно установить флаги, используемыеdlopen()
, с помощью функцийsys.getdlopenflags()
иsys.setdlopenflags()
. (Внесено Брэмом Столком.)Встроенная функция
pow()
больше не поддерживает 3 аргумента при передаче чисел с плавающей точкой.pow(x, y, z)
возвращает(x**y) % z
, но это никогда не пригодится для чисел с плавающей точкой, а конечный результат непредсказуемо меняется в зависимости от платформы. Вызов типаpow(2.0, 8.0, 7.0)
теперь будет вызывать исключениеTypeError
.
Благодарности¶
Автор благодарит следующих людей за предложения, исправления и помощь в работе над различными вариантами этой статьи: Фреду Бреммеру, Киту Бриггсу, Эндрю Далку, Фреду Л. Дрейку-мл, Карел Феллингер, Дэвид Гуджер, Марк Хэммонд, Стивен Хансен, Майкл Хадсон, Джек Янсен, Марк-Андре Лембург, Мартин фон Лёвис, Фредрик Лундх, Майкл Маклей, Ник Мэтьюсон, Пол Мур, Густаво Нимейер, Дон О’Доннелл, Йоонас Пааласма, Тим Питерс, Йенс Куэйд, Том Рейнхардт, Нил Шеменауэр, Гвидо ван Россум, Грег Уорд, Эдвард Уэлборн.