Що нового в 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 змінює цей алгоритм на такий, який дає ті самі результати в більшості випадків, але дає більш корисні результати для дійсно складних графів успадкування.)

  1. 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].

  2. 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 оператора tryfinally; читайте 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 the PyNumberMethods 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 or tm_year. The enhanced functions include stat(), fstat(), statvfs(), and fstatvfs() in the os module, and localtime(), gmtime(), and strptime() in the time 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, and Q 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 Yee pydoc.)

  • Various bugfixes and performance improvements have been made to the SRE engine underlying the re module. For example, the re.sub() and re.split() functions have been rewritten in C. Another contributed patch speeds up certain Unicode character ranges by a factor of two, and a new finditer() 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 to rfc2822.) 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 new Differ class for producing human-readable lists of changes (a «delta») between two sequences of lines of text. There are also two generator functions, ndiff() and restore(), 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, and ascii_uppercase were added to the string module. There were several modules in the standard library that used string.letters to mean the ranges A-Za-z, but that assumption is incorrect when locales are in use, because string.letters varies depending on the set of legal characters defined by the current locale. The buggy modules have all been fixed to use ascii_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 a MimeTypes class, which takes a list of filenames to be parsed. (Contributed by Fred L. Drake, Jr.)

  • A Timer class was added to the threading 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 to Py_TPFLAGS_HAVE_GC.

  • Використовуйте PyObject_GC_New() або PyObject_GC_NewVar() для виділення

    об’єктів і PyObject_GC_Del(), щоб звільнити їх.

  • Rename PyObject_GC_Init() to PyObject_GC_Track() and PyObject_GC_Fini() to PyObject_GC_UnTrack().

  • Remove PyGC_HEAD_SIZE from object size calculations.

  • Remove calls to PyObject_AS_GC() and PyObject_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 and METH_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 uses METH_VARARGS. Also, the old METH_OLDARGS style of writing C methods is now officially deprecated.

  • Two new wrapper functions, PyOS_snprintf() and PyOS_vsnprintf() were added to provide cross-platform implementations for the relatively new snprintf() and vsnprintf() C lib APIs. In contrast to the standard sprintf() and vsprintf() 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 the in operator, but these features were rarely used and therefore buggy. The tolist() method and the start, stop, and step attributes are also being deprecated. At the C level, the fourth argument to the PyRange_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.

Подяки

Автор хотів би подякувати наступним особам за пропозиції, виправлення та допомогу з різними чернетками цієї статті: Фред Бреммер, Кіт Бріггс, Ендрю Далк, Фред Л. Дрейк молодший, Карел Феллінгер, Девід Гуджер, Марк Хаммонд, Стівен Хансен, Майкл Хадсон, Джек Янсен, Марк-Андре Лембург, Мартін фон Левіс, Фредрік Лунд, Майкл МакЛей, Нік Метьюсон, Пол Мур, Густаво Німейєр, Дон О’Доннелл, Йоонас Пааласма, Тім Пітерс, Йенс Куейд, Том Рейнхардт, Ніл Шеменауер, Гвідо ван Россум, Грег Уорд, Едвард Велборн.