Що нового в Python 2.2¶
- Автор:
A.M. Kuchling
вступ¶
У цій статті пояснюється нові функції в Python 2.2.2, випущеному 14 жовтня 2002 року. Python 2.2.2 — це випуск Python 2.2 з виправленням помилок, спочатку випущений 21 грудня 2001 року.
Python 2.2 можна розглядати як «випуск для очищення». Є деякі функції, такі як генератори та ітератори, які є абсолютно новими, але більшість змін, хоч і значних і далекосяжних вони можуть бути, спрямовані на очищення нерівностей і темних кутів мовного дизайну.
Ця стаття не намагається надати повну специфікацію нових функцій, натомість надає зручний огляд. Щоб отримати повну інформацію, зверніться до документації для Python 2.2, наприклад Довідник бібліотеки Python і Довідковий посібник Python. Якщо ви хочете зрозуміти повну реалізацію та обґрунтування дизайну для зміни, зверніться до PEP для конкретної нової функції.
PEP 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)
— це метод, який отримує значення атрибута з object.__set__(object, value)
встановлює атрибут 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.
Більше нових функцій, таких як слоти та властивості, також реалізовано як нові типи дескрипторів, і не важко написати клас дескриптора, який робить щось нове. Наприклад, можна було б написати клас дескрипторів, який би дозволив написати передумови та постумови методу в стилі Ейфеля. Клас, який використовує цю функцію, можна визначити так:
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'
Зверніть увагу, як ви отримуєте AttributeError
під час спроби призначити атрибут, не вказаний у __slots__
.
PEP 234: Ітератори¶
Іншим значним доповненням до 2.2 є ітераційний інтерфейс як на рівнях C, так і на Python. Об’єкти можуть визначати, як вони можуть бути зациклені абонентами.
In Python versions up to 2.1, the usual way to make for item in obj
work is
to define a __getitem__()
method that looks something like this:
def __getitem__(self, index):
return <next item>
__getitem__()
is more properly used to define an indexing operation on an
object so that you can write obj[5]
to retrieve the sixth element. It’s a
bit misleading when you’re using this only to support for
loops.
Consider some file-like object that wants to be looped over; the index
parameter is essentially meaningless, as the class probably assumes that a
series of __getitem__()
calls will be made with index incrementing by
one each time. In other words, the presence of the __getitem__()
method
doesn’t mean that using file[5]
to randomly access the sixth element will
work, though it really should.
In Python 2.2, iteration can be implemented separately, and __getitem__()
methods can be limited to classes that really do support random access. The
basic idea of iterators is simple. A new built-in function, iter(obj)
or iter(C, sentinel)
, is used to get an iterator. iter(obj)
returns
an iterator for the object obj, while iter(C, sentinel)
returns an
iterator that will invoke the callable object C until it returns sentinel to
signal that the iterator is done.
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 - Ітератори
Написаний Ка-Пінг Йі та 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 (a маршрут, який веде лицаря до кожного поля $NxN$ шахівниці, не відвідуючи жодного поля двічі).
The idea of generators comes from other programming languages, especially Icon (https://www2.cs.arizona.edu/icon/), where the idea of generators is central. In Icon, every expression and function call behaves like a generator. One example from «An Overview of the Icon Programming Language» at https://www2.cs.arizona.edu/icon/docs/ipd266.htm gives an idea of what this looks like:
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 - Зміна оператора ділення
Автори Моше Цадка та Гвідо ван Россум. Реалізовано Гвідо ван Россумом..
Зміни Unicode¶
Підтримку Python Unicode було дещо вдосконалено у версії 2.2. Рядки Unicode зазвичай зберігаються як UCS-2, як 16-розрядні цілі числа без знаку. Python 2.2 також можна скомпілювати для використання UCS-4, 32-розрядних цілих чисел без знаку, як внутрішнього кодування, надаючи --enable-unicode=ucs4
до сценарію конфігурації. (Також можна вказати --disable-unicode
, щоб повністю вимкнути підтримку 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(назва_файлу).st_size
.Оригінальний патч для цієї функції надав Нік Метьюсон.
Профайлер Python було значно перероблено та виправлено різні помилки в його виводі. (Надано Фредом Л. Дрейком молодшим і Тімом Пітерсом.)
Модуль
socket
можна скомпільувати для підтримки IPv6; вкажіть параметр--enable-ipv6
для сценарію налаштування Python. (Надав Дзюн-ічіро «ітоджун» Хагіно.)Two new format characters were added to the
struct
module for 64-bit integers on platforms that support the C long long type.q
is for a signed 64-bit integer, andQ
is for an unsigned one. The value is returned in Python’s long integer type. (Contributed by Tim Peters.)В інтерактивному режимі інтерпретатора є нова вбудована функція
help()
, яка використовує модульpydoc
, представлений у Python 2.1, для надання інтерактивної довідки.help(object)
відображає будь-який доступний текст довідки про object.help()
без аргументу передає вас до утиліти онлайн-довідки, де ви можете вводити назви функцій, класів або модулів, щоб прочитати їхній текст довідки. (Надано Гвідо ван Россумом, використовуючи модуль Ka-Ping Yeepydoc
.)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, «Безпечний SMTP через 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, виконайте такі дії:
Rename
Py_TPFLAGS_GC
toPy_TPFLAGS_HAVE_GC
.- Використовуйте
PyObject_GC_New()
абоPyObject_GC_NewVar()
для виділення об’єктів і
PyObject_GC_Del()
, щоб звільнити їх.
- Використовуйте
Rename
PyObject_GC_Init()
toPyObject_GC_Track()
andPyObject_GC_Fini()
toPyObject_GC_UnTrack()
.Remove
PyGC_HEAD_SIZE
from object size calculations.Remove calls to
PyObject_AS_GC()
andPyObject_FROM_GC()
.Нову послідовність формату
et
додано доPyArg_ParseTuple()
;et
приймає як параметр, так і ім’я кодування та перетворює параметр у вказане кодування, якщо параметр виявляється рядком Юнікоду, або залишає його в спокої, якщо це 8-бітовий рядок, припускаючи, що він уже є у потрібному кодуванні. Це відрізняється від символу форматуes
, який припускає, що 8-бітні рядки мають типове кодування ASCII Python, і перетворює їх у вказане нове кодування. (Надано M.-A. Lemburg і використовується для підтримки MBCS у Windows, описаної в наступному розділі.)A different argument parsing function,
PyArg_UnpackTuple()
, has been added that’s simpler and presumably faster. Instead of specifying a format string, the caller simply gives the minimum and maximum number of arguments expected, and a set of pointers to PyObject* variables that will be filled in with argument values.Two new flags
METH_NOARGS
andMETH_O
are available in method definition tables to simplify implementation of methods with no arguments or a single untyped argument. Calling such methods is more efficient than calling a corresponding method that usesMETH_VARARGS
. Also, the oldMETH_OLDARGS
style of writing C methods is now officially deprecated.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 виявив, що було застосовано 527 патчів і 683 виправлені помилки між Python 2.1 і 2.2; 2.2.1 застосовано 139 патчів і виправлено 143 помилки; 2.2.2 застосовано 106 патчів і виправлено 82 помилки. Ймовірно, ці цифри занижені.
Деякі з найбільш помітних змін:
Код порту MacOS для Python, який підтримує Джек Янсен, тепер зберігається в основному дереві Python CVS, і внесено багато змін для підтримки MacOS X.
Найсуттєвішою зміною є можливість створювати Python як фреймворк, увімкнений шляхом надання опції
--enable-framework
до сценарію конфігурації під час компіляції Python. За словами Джека Янсена, «це встановлює самодостатню інсталяцію Python, а також фреймворк OS X «клей» у/Library/Frameworks/Python.framework
(або в інше розташування). це додаткова перевага (насправді, є недолік, що вам потрібно змінити свій ШЛЯХ, щоб мати змогу знайти 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.Додатковий аргумент flags було додано до вбудованої функції
compile()
, тому поведінку операторів__future__
тепер можна правильно спостерігати в змодельованих оболонках, таких як представлені IDLE та інші розробки середовищ. Це описано в PEP 264. (Надав Майкл Хадсон.)Нова ліцензія, представлена разом із Python 1.6, не була сумісною з GPL. Це виправлено деякими незначними текстовими змінами в ліцензії 2.2, тому тепер можна знову вставляти Python у програму GPL. Зауважте, що сам Python не підпадає під ліцензію GPL, а натомість діє під ліцензією, яка, по суті, еквівалентна ліцензії BSD, як і було завжди. Зміни ліцензії також було застосовано до випусків Python 2.0.1 і 2.1.1.
Коли в Windows надається ім’я файлу Unicode, 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
.
Подяки¶
Автор хотів би подякувати наступним особам за пропозиції, виправлення та допомогу з різними чернетками цієї статті: Фред Бреммер, Кіт Бріггс, Ендрю Далк, Фред Л. Дрейк молодший, Карел Феллінгер, Девід Гуджер, Марк Хаммонд, Стівен Хансен, Майкл Хадсон, Джек Янсен, Марк-Андре Лембург, Мартін фон Левіс, Фредрік Лунд, Майкл МакЛей, Нік Метьюсон, Пол Мур, Густаво Німейєр, Дон О’Доннелл, Йоонас Пааласма, Тім Пітерс, Йенс Куейд, Том Рейнхардт, Ніл Шеменауер, Гвідо ван Россум, Грег Уорд, Едвард Велборн.