Обзор новых возможностей в Python 2.6 и 3.0 | jenyay.net

Обзор новых возможностей в Python 2.6 и 3.0

Оглавление

1. Введение
2. Строки и Unicode
3. Функция print()
4. Форматирование строк
5. Свойства
6. Абстрактные базовые классы
7. Обработка исключений
8. Оператор with
9. Математика
10. Разное
11. Заключение
12. Ссылки
Комментарии

1. Введение

В сентябре 2008 года должны выйти сразу две версии языка Python - 2.6 и 3.0. Версия 3.0 потеряет обратную совместимость с линейкой 2.x, но облегчить переход на новую ветку должна версия 2.6, в которой будут реализованы основные возможности из Python 3.0, но в которой еще сохранится обратная совместимость с предыдущими версиями. Таким образом, в версии 2.6 уже можно будет пользоваться многими возможностями Python 3.0, но в то же время старый код будет продолжать работать, и будет время для перехода на Python 3.0.

В этой статье мы с вами рассмотрим основные изменения, которые произошли в Python 2.6 и 3.0 по сравнению с Python 2.5. Для запуска примеров использовались первые бета-версии Python 2.6 и 3.0, поэтому к выходу финальной версии еще может что-то измениться.

2. Строки и Unicode

В Python 3.0 все строки стали юникодными. С формальной точки зрения строки остались экземплярами класса str, а unicode исчез как класс. :) Не стало даже спецификатора u перед кавычками. В Python 2.6 по-прежнему остались два типа строк - str и unicode.

Кроме того, в Python 3.0 изменилась встроенная функция str(), теперь она, по сути, заменяет старую функцию unicode() и принимает те же три параметра - объект для преобразования в строку, строку, определяющую кодировку, и строку, определяющую поведение функции на случай, если str() не сможет преобразовать какие-то символы.

3. Функция print()

В Python 3.0 не стало ключевого слова print, зато появилась встроенная функция print(). В Python 2.6 ключевое слово print осталось, но добавилась возможность использовать одноименную функцию, для чего надо импортировать print_function из модуля __future__ :

from __future__ import print_function

Напомню, что это импорт из модуля __future__ должен стоять в самом начале исходника, даже до импорта других модулей, иначе Python напишет такую ошибку:

File "H:\Черновики\Python 2.6 & 3.0\print_2.py", line 3
from __future__ import print_function
SyntaxError: from __future__ imports must occur at the beginning of the file

Функция print() определена следующим образом:

print([object, ...][, sep=' '][, end='\n'][, file=sys.stdout])

Сначала идут объекты, которые надо вывести.
sep - разделитель между объектами, по умолчанию - пробел
end - строка, выводимая в конце, по умолчанию - символ перевода строки \n.
file определяет то, куда будет выводиться строка. По умолчанию вывод направлен в консоль.

Давайте сравним результаты работы двух исходников (оба запускались в Python 2.6). В первом случае print - ключевое слово.

print ("test")
print ()
print "test1", "test2"
print ("test1", "test2")

В результате получим:

test
()
test1 test2
('test1', 'test2')

И то же самое, но если используется функция print():

from __future__ import print_function

print ("test")
print ()
print ("test1", "test2")

Результатом работы этого кода будет:

test

test1 test2

Как видите, при переходе от ключевого слова к функции надо быть особенно аккуратным при использовании кортежей.

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

from __future__ import print_function

print ("test1", "test2", "test3")
print ("test4", "test5", "test6", sep = " # ")
print ("test7", "test8", "test9", end = "###")
print ("test10", "test11", "test12")

fp = file ("print_test.txt", "w")
print ("Тест1", "Тест2", "Тест3", file = fp)
fp.close()

Если запустить этот скрипт из-под Python 2.6, то в результате в консоли будет выведено:

test1 test2 test3
test4 # test5 # test6
test7 test8 test9###test10 test11 test12

Кроме того, будет создан файл с именем print_test.txt, который будет содержать строку "Тест1 Тест2 Тест3".

4. Форматирование строк

В Python 2.6 и 3.0 появился новый способ форматирования строк, причем способ с помощью оператора % тоже работает. Новый способ форматирования напоминает функцию String.Format() из .NET Framework.

Теперь у класса str есть новый метод - format(), работу которого мы сейчас и рассмотрим. Думаю, что не стоит приводить полный синтаксис форматирования (его вы можете найти в документации), а лучше рассмотрим достаточно большое количество примеров.

Начнем с простого:

listval = [1.1, 2.2, 3.3]
print ("{0} | {1} | {2} | {3}".format (45, 123.4, "abyrvalg", listval) )

В результате выполнения этого кода на экран будет выведен следующий текст:

45 | 123.4 | abyrvalg | [1.1000000000000001, 2.2000000000000002, 3.2999999999999998]

Таким образом метод format() подставил вместо выражений {0}, {1}, ..., {3} значения параметров, которые были ему переданы. Заметьте, что в строке форматирования параметры в фигурных скобках не обязательно должны идти в той последовательности, в которой они передаются в метод format(). Например, вполне нормально будет работать следующий код:

print ("{1} | {0} | {3} | {2}".format (45, 123.4, "abyrvalg", listval) )

В результате на экран будет выведена строка

123.4 | 45 | [1.1000000000000001, 2.2000000000000002, 3.2999999999999998] | abyrvalg

Для каждого из параметров внутри фигурных скобок после двоеточия можно задать способ форматирования. В том числе, можно задать формат и систему счисления, в которой будут выводиться числа. Например:

>>> # Обычный десятичный формат
>>> print ("Decimal: {0:d}".format (45) )
Decimal: 45

>>> # Двоичный формат
>>> print ("Binary: {0:b}".format (45) )
Binary: 101101

>>> # Восьмиричный формат
>>> print ("Octal: {0:o}".format (45) )
Octal: 55

>>> # Шестнадцатиричный формат
>>> print ("Hex: {0:x}".format (45) )
Hex: 2d

>>> print ("Hex: {0:X}".format (45) )
Hex: 2D


>>> # Основной формат, используемый по умолчанию
>>> print ("General: {0:g}".format (123.4) )
General: 123.4

>>> # Экспоненциальный формат
>>> print ("Exponent: {0:e}".format (45) )
Exponent: 4.500000e+01

>>> print ("Exponent: {0:E}".format (45) )
Exponent: 4.500000E+01

>>> # Вывод процентов. Число будет умножено на 100
>>> print ("Percent: {0:%}".format (0.25) )
Percent: 25.000000%

Можно также задать размер (ширину) "ячейки", отводимой для числа. Если для вывода числа требуется меньше символов, чем ему отведено, то оставшиеся символы заполняются пробелами. В этом случае можно использовать дополнительный параметр, определяющий выравнивание числа внутри "ячейки". Если число не влезает в выделенную ячейку, то параметр, задающий ширину игнорируется.

В следующей группе примеров для числа отводится 15 символов.

>>> # Используется выравнивание по умолчанию (по правому краю)
>>> print ("|{0:15}|".format (-19) )
|            -19|

>>> # То же выравнивание можно задать явно с помощью символа '>'
>>> print ("|{0:>15}|".format (-19) )
|            -19|

>>> # Выравнивание по левому краю задается с помощью символа '<'
>>> print ("|{0:<15}|".format (-19) )
|-19            |

>>> # Выравнивание по центру задается с помощью символа '^'
>>> print ("|{0:^15}|".format (-19) )
|      -19      |

>>> # Знак числа будет выровнен по левому краю, а само число по правому. Для этого используется символ '='
>>> print ("|{0:=15}|".format (-19) )
|-            19|

Кроме того, можно задать точность вывода дробных чисел, а точнее, задать количество выводимых цифр. Этот параметр записывается после точки в строке форматирования. В следующей группе примеров задание точности объединено с заданием ширины "ячейки".

>>> print ("|{0:15.4g}|".format (-22.56789) )
|         -22.57|

>>> print ("|{0:15.5g}|".format (-22.56789) )
|        -22.568|

>>> print ("|{0:15.15g}|".format (-22.56789) )
|      -22.56789|

>>> print ("|{0:15.1g}|".format (-22.56789) )
|         -2e+01|

Порядок следования подстановочных параметров в строке форматирования можно задавать не только с помощью порядкового номера параметра метода format(), но и давая им имена, например:

>>> print ("x = {x}; y = {y}".format (x = 124.2, y = 56.8) )
x = 124.2; y = 56.8

Здесь параметрам мы дали имена x и y, которые также используются в фигурных скобках.

Можно также комбинировать эти два способа. Хотя мне кажется, что это будет только запутывать код, однако возможность такая есть:

>>> print ("x = {0}; y = {y}".format (124.2, y = 56.8) )
x = 124.2; y = 56.8

Кроме перечисленных у метода format() есть еще несколько настроек форматирования, который мы рассматривать не будем.

5. Свойства

В новых версиях Python появился новый, более удобный способ задания свойств. До Python 2.6 и 3.0 класс со свойствами мог выглядеть примерно так:

class Foo(object):
    def __init__(self, xval):
        print ("*** Foo.__init__()")
        self._x = xval

    def getx(self):
        print ("*** X.Getter(): x = %d" % self._x )
        return self._x

    def setx(self, value):
        self._x = value
        print ("*** X.Setter(): new x value = %d" % self._x )

    x = property (getx, setx)

f = Foo(123)
print (f.x)

f.x = 345
print (f.x)

Теперь тот же код можно переписать следующим образом:

class Foo(object):
    def __init__(self, xval):
        print ("*** Foo.__init__()")
        self._x = xval

    @property
    def x(self):
        print ("*** X.Getter(): x = %d" % self._x )
        return self._x

    @x.setter
    def x(self, value):
        self._x = value
        print ("*** X.Setter(): new x value = %d" % self._x )

f = Foo(123)
print (f.x)

f.x = 345
print (f.x)

Внутри класса метод для получения значения свойства (getter) можно пометить декоратором @property, а метод для установки значения свойства (setter) - декоратором, имя которого представляет собой имя_свойства.setter.

Кроме того, по аналогии с setter так же можно объявлять deleter, который вызывается, если к свойству применить функцию del(). Вот пример использования такого свойства:

class Foo(object):
    def __init__(self, xval):
        print ("*** Foo.__init__()")
        self._x = xval

    @property
    def x(self):
        print ("*** X.Getter(): x = %d" % self._x )
        return self._x

    @x.setter
    def x(self, value):
        self._x = value
        print ("*** X.Setter(): new x value = %d" % self._x )

    @x.deleter
    def x(self):
        print ("*** X.Deleter()")
        del self._x

f = Foo(123)
print (f.x)

f.x = 345
print (f.x)

del f.x

В результате выполнения этого кода в консоль будут выведены следующие строки:

*** Foo.__init__()
*** X.Getter(): x = 123
123
*** X.Setter(): new x value = 345
*** X.Getter(): x = 345
345
*** X.Deleter()

6. Абстрактные базовые классы

Теперь, как и в других объектно-ориентированных языках программирования, в Python появилась возможность делать так называемые абстрактные базовые классы. Так называются классы, по которым нельзя сделать объекты, потому что в них не реализованы некоторые методы или свойства (абстрактные методы и свойства). Для создания объекта необходимо сначала создать класс, производный от абстрактного класса, внутри которого (пере)определить абстрактные методы и свойства.

Для работы с абстрактными классами появился новый модуль - abc (сокращение от слов Abstract Base Classes).

Чтобы объявить класс абстрактным в Python 2.6, необходимо переменной __metaclass__ в его определении присвоить значение abc.ABCMeta.

from abc import ABCMeta

class Foo (object):
    __metaclass__ = ABCMeta

В Python 3.0 абстрактный класс должен быть объявлен следующим образом:

class Foo (metaclass=ABCMeta):

Не понятно почему разработчики сделали разные способы объявления классов абстрактными в версиях 2.6 и 3.0, причем способы способ с __metaclass__ = ABCMeta не работает в Python 3.0, а способ с class Foo (metaclass=ABCMeta) не работает в Python 2.6.

Чтобы сделать абстрактный метод внутри класса, его надо пометить декоратором @abs.abstractmethod.

Рассмотрим примеры использования абстрактных классов и методов.

Python 2.6:

from abc import ABCMeta
from abc import abstractmethod

class Foo (object):
    __metaclass__ = ABCMeta

    def __init__(self):
        print ("*** Foo.__init__()")

    @abstractmethod
    def run (self):
        print ("*** Foo.run()")

class Bar (Foo):
    def __init__ (self):
        print ("*** Bar.__init__()")
        Foo.__init__ (self)

    def run (self):
        print ("*** Bar.run()")

if __name__ == "__main__":
    bar_obj = Bar()
    bar_obj.run()

Python 3.0:

#-*- coding: utf-8 -*-

from abc import ABCMeta
from abc import abstractmethod

class Foo (metaclass = ABCMeta):
    def __init__(self):
        print ("*** Foo.__init__()")

    @abstractmethod
    def run (self):
        print ("*** Foo.run()")

class Bar (Foo):
    def __init__ (self):
        print ("*** Bar.__init__()")
        Foo.__init__ (self)

    def run (self):
        print ("*** Bar.run()")

class Bar2 (Foo):
    def __init__ (self):
        print ("*** Bar2.__init__()")
        Foo.__init__ (self)

if __name__ == "__main__":
    bar_obj = Bar()
    bar_obj.run()

В результате выполнения этого скрипта мы получим следующий результат:

*** Bar.__init__()
*** Foo.__init__()
*** Bar.run()

А теперь, мы попытаемся создать экземпляр класса Foo:

foo_obj = Foo()

В результате выполнения этой строки будет вызвано исключение:

Traceback (most recent call last):
File "H:\Черновики\Python 2.6 & 3.0\abstract_1.py", line 33, in <module>
foo_obj = Foo()
TypeError: Can't instantiate abstract class Foo with abstract methods run

Теперь создадим другой производный от Foo класс - Bar2, но "забудем" переопеределить абстрактный метод run():

class Bar2 (Foo):
    def __init__ (self):
        print ("*** Bar2.__init__()")
        Foo.__init__ (self)

Если попытаться создать экземпляр класса Bar2:

bar2_obj = Bar2()

то будет вызвано то же исключение:

Traceback (most recent call last):
File "H:\Черновики\Python 2.6 & 3.0\abstract_1.py", line 35, in <module>
bar2_obj = Bar2()
TypeError: Can't instantiate abstract class Bar2 with abstract methods run

Теперь попробуем объявим класс абстрактным, но без абстрактных методов, и попытаемся создать экземпляр такого класса в стиле Python 2.6 (дальше во всех примерах этого раздела будем использовать именно Python 2.6):

from abc import ABCMeta
from abc import abstractmethod

class Foo (object):
    __metaclass__ = ABCMeta

    def __init__(self):
        print ("*** Foo.__init__()")

if __name__ == "__main__":
    foo_obj = Foo()

Как ни странно, объект будет успешно создан и мы увидим строку

*** Foo.__init__()

Аналогично ведет себя и Python 3.0. Не знаю ошибка ли это в первых бетах или так и было задумано, но, ИМХО, такое поведение нелогично. Если уж объявлять класс абстрактным, то надо всегда запрещать создавать экземпляры этого класса независимо от того есть ли у него абстрактные методы (или свойства) или нет.

Кроме методов абстрактными могут быть также и свойства. Чтобы пометить свойство как абстрактное, его getter надо пометить декоратором @abc.abstractproperty. Например:

from abc import ABCMeta, abstractproperty

class Foo (object):
    __metaclass__ = ABCMeta

    def __init__ (self):
        print ("*** Foo.__init__()")
        self._x = 0

    @abstractproperty
    def x (self):
        pass
 

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

class Bar (Foo):
    def __init__ (self):
        print ("*** Bar.__init__()")
        Foo.__init__ (self)

    @property
    def x (self):
        return self._x

Если теперь попытаемся создать объект класса Foo, то снова получим исключение:

Traceback (most recent call last):
File "H:\Черновики\Python 2.6 & 3.0\abstract_4.py", line 21, in <module>
foo_obj = Foo()
TypeError: Can't instantiate abstract class Foo with abstract methods x

То же самое произойдет, если попытаться создать экземпляр производного от Foo класса, но без переопределения свойства x.

Если мы хотим сделать абстрактным также и setter свойства, то придется воспользоваться старым стилем определения свойств:

from abc import ABCMeta, abstractproperty

class Foo (object):
    __metaclass__ = ABCMeta

    def __init__ (self):
        print ("*** Foo.__init__()")
        self._x = 0

    def getx (self):
        pass

    def setx (self, value):
        pass

    x = abstractproperty(getx, setx)

Использование этого класса может выглядеть примерно так (здесь мы уже можем воспользоваться новым способом создания свойств):

class Bar (Foo):
    def __init__ (self):
        print ("*** Bar.__init__()")
        Foo.__init__ (self)

    @property
    def x (self):
        return self._x

    @x.setter
    def x (self, value):
        self._x = value


bar_obj = Bar()
bar_obj.x = 3
print (bar_obj.x)

В результате работы этого скрипта получим:

*** Bar.__init__()
*** Foo.__init__()
3

7. Обработка исключений

Во-первых, небольшие изменения произошли и в синтаксисе при обработке исключений. Вот как раньше выглядел код при ожидании одного исключения (в нашем случае TypeError):

try:
    raise TypeError("111")
except TypeError, exc:
    print (exc)

В Python 2.6 этот метод будет работать, а вот в Python 3.0 будет ругаться на неверный синтаксис. Чтобы этот код работал и в Python 2.6, и в Python 3.0 (но не в Python 2.5 и ниже), в строке с ключевым словом except запятую необходимо заменить на ключевое слово as:

try:
    raise TypeError("111")
except TypeError as exc:
    print (exc)

В случе, если надо отлавливать несколько исключений, данный пример будет выглядеть следующим образом:

try:
    #raise TypeError("111")
    raise ValueError("222")
except (TypeError, ValueError) as exc:
    print (exc)

Если верить документации, то такое изменение синтаксиса было сделано, чтобы лучше визуально отделить переменную типа исключения от списка ожидаемых исключений.

Кроме того, теперь в Python 3.0 классы всех вызываемых исключений обязаны быть производными от одного базового класса - BaseException, в противном случае оператор raise вместо ожидаемого исключения вызовет исключение типа TypeError. Однако в документации для пользовательских исключений в качестве базового класса рекомендуют использовать класс Exception, а класс BaseException оставить для встроенных исключений.

Таким образом, теперь использование пользовательских исключений должно выглядеть примерно так:

class FooException (Exception):
    def __init__ (self, val):
        Exception.__init__ (self, val)

try:
    raise FooException ("My Exception")
except FooException as exc:
    print (exc)

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

class FooException ():
    def __init__ (self, val):
        pass

try:
    raise FooException ("My Exception")
except FooException as exc:
    print (exc)

В Python 3.0 выражение raise FooException ("My Exception") вызовет исключение:

TypeError: catching classes that do not inherit from BaseException is not allowed

В Python 2.6 без использования параметра командной строки -3 (о нем более подробно мы поговорим ниже) этот пример будет работать без проблем, а при включении параметра -3 выдаст сразу два предупреждения:

H:\Черновики\Python 2.6 & 3.0\exception_6.py:8: DeprecationWarning: exceptions must derive from BaseException in 3.x
raise FooException ("My Exception")
H:\Черновики\Python 2.6 & 3.0\exception_6.py:9: DeprecationWarning: catching classes that don't inherit from BaseException is not allowed in 3.x
except FooException as exc:

8. Оператор with

Оператор with представляет собой некоторый аналог обертывания объекта в try / finally. Он был введен еще в Python 2.5, но для его использования необходимо было импортировать with_statement из модуля __future__ :

from __future__ import with_statement

Начиная, с Python 2.6 этого не требуется, и with стал обычным ключевым словом.

Суть его состоит в том, что для объекта, подаваемого на вход оператора with, сначала вызывается специальный метод - __enter__(), а перед выходом из оператора вызывается метод __exit__(), причем метод __exit__() вызывается независимо от того был ли блок внутри оператора with выполнен до конца или он был прерван исключением.

Давайте рассмотрим пример и на нем разберемся с синтаксисом оператора with. Поддержку оператора with имеет, например, класс File, поэтому, чтобы быть уверенными, что файл в любом случае будет закрыт после записи, мы можем написать такой код:

fname = "test.txt"

with file (fname, "w") as fp:
    fp.write ("Hello, with")

with file (fname, "w") as fp:
    fp.write ("Hello, with — 2")

На входе оператора with мы создаем объект File с помощью выражения file (fname, "w") и присваиваем его переменной fp с помощью выражения as fp, которое, строго говоря, является необязательным, если мы не хотим использовать созданный объект внутри блока оператора.

В этом примере блок with специально был использован дважды, чтобы показать, что файл test.txt после блока with закрыт, иначе при повторном открытии файла на запись произошло бы исключение. Как мы можем убедиться, файл test.txt будет создан и в нем будет записана строка «Hello, with — 2», т.е. вторая запись в файл будет успешно выполнена.

Давайте теперь создадим свой класс объектов, которые смогут использовать оператор with. Рассмотрим следующий код:

class Foo (object):
    def __init__ (self):
        print ("*** Foo.__init__()")

    def __enter__ (self):
        print ("*** Foo.__enter__()")
        return self

    def __exit__ (self, exc_type, exc_value, traceback):
        print ("*** Foo.__exit__()")
        print ("*** exc_type: {0}\n*** exc_value: {1}\n*** traceback: {2}".format (exc_type, exc_value, traceback) )

    def Run (self):
        print ("*** Foo.Run()")

if __name__ == "__main__":
    with Foo() as foo_obj:
        print ("With begin")
        foo_obj.Run()
        print ("With end")

    print ("After With")

Здесь мы создали класс Foo и объявили в нем методы __enter__() и __exit__() . Начнем с первого.

У метода __enter__() нет никаких параметров кроме self. При этом метод __enter__() должен возвращать значение, которое будет присвоено переменной, стоящей после ключевого слова as оператора with, т.е. в нашем случае foo_obj. Как видите, оператор as не просто присваивает переменной значение выражения внутри with. Мы могли бы вернуть из __enter__() объект другого класса.

У метода __exit__() кроме self есть еще три параметра, которые описывают возникшее внутри блока with исключение.

exc_type - Тип исключения
exc_value - Значение исключения
traceback - трассировка вызовов

Если блок внутри with завершился без исключений, то все эти параметры равны None.

Теперь посмотрим результат работы последнего примера:

*** Foo.__init__()
*** Foo.__enter__()
With begin
*** Foo.Run()
With end
*** Foo.__exit__()
*** exc_type: None
*** exc_value: None
*** traceback: None
After With

Итак, первое, что делает оператор with, создает объект класса Foo и вызывает его метод __enter__() . Затем полностью выполняется блок внутри with и вызывается метод __exit__() , значениями аргументов которого являются три None.

Изменим пример так, чтобы метод Run() вызывал исключение:

class Foo (object):
    def __init__ (self):
        print ("*** Foo.__init__()")

    def __enter__ (self):
        print ("*** Foo.__enter__()")
        return self

    def __exit__ (self, exc_type, exc_value, traceback):
        print ("*** Foo.__exit__()")
        print ("*** exc_type: {0}\n*** exc_value: {1}\n*** traceback: {2}".format (exc_type, exc_value, traceback) )

    def Run (self):
        raise AssertionError ("Run error")


if __name__ == "__main__":
    with Foo() as foo_obj:
        print ("With begin")
        foo_obj.Run()
        print ("With end")

    print ("After With")

И его результат работы:

*** Foo.__init__()
*** Foo.__enter__()
With begin
*** Foo.__exit__()
*** exc_type: <type 'exceptions.AssertionError'>
*** exc_value: Run error
*** traceback: <traceback object at 0x00C3D620>
Traceback (most recent call last):
File "H:\Черновики\py_26_with_3.py", line 22, in <module>
foo_obj.Run()
File "H:\Черновики\py_26_with_3.py", line 16, in Run
raise AssertionError ("Run error")
AssertionError: Run error

Таким образом с помощью оператора with мы действительно гарантируем, что метод __exit__() будет вызван. Думаю, дополнительных комментариев здесь не требуется.

9. Математика

Еще одно заметное нововведение в Python 3.0 (в 2.6 этого нет) - это то, что для целочисленного деления теперь нужно будет использовать оператор // . Оператор / теперь используется для дробного деления.

Если раньше в результате выполнения строк

print (3/2)
print (4/2)

мы бы получили в качестве результата

то в Python 3.0 оператор / предназначен для дробного деления, результатом которого является объект типа float, и в результате выполнения этого же кода, мы получим:

Для того, чтобы выполнить целочисленное деление, этот пример надо переписать в следующем виде:

print (3//2)
print (4//2)

В результате выполнения этих строк получим опять

Гиперболические функции acosh(), asinh() и atanh() теперь есть не только в модуле cmath, но и в модуле math (это уже относится и к Python 3.0, и к Python 2.6).

Были введены особые числа с плавающей точкой - nan (not a number), +inf и -inf (соответственно, + и - бесконечности). Создавать их можно с помощью встроенной функции float():

>>> float ('nan')
nan

>>> float ('-inf')
-inf

>>> float ('inf')
inf

Рассмотрим несколько простых примеров с использованием этих чисел:

>>> a = float ('inf')
>>> a
inf

>>> # Бесконечность, умноженная на 0
>>> a * 0
nan

>>> # Бесконечность, деленная на бесконечность
>>> a / a
nan

>>> # Очень большое число
>>> a=1.0e999
>>> a
inf

>>> # Бесконечность, умноженная на -1
>>> b = -a
>>> b
-inf

>>> # Деление бесконечностей разных знаков
>>> a/b
nan

>>> # Сумма бесконечностей разных знаков
>>> a + b
nan

Для того, чтобы определить является ли число бесконечностью или NaN, в модулях math и cmath созданы специальные функции - isnan() и isinf(), которые возвращают True, если число является NaN или бесконечностью соответственно, и False в противном случае.

В модуле math появилась новая функция - log1p(x), которая вычисляет натуральный логарифм от 1 + x.

>>> math.log1p (0)
0.0

>>> math.log1p (1)
0.69314718055994529

>>> math.log (2)
0.69314718055994529

В модуле cmath появились новые функции для работы с комплексными числами.

Функция polar() возвращает значение амплитуды и фазы комплексного числа в полярной системе координат:

>>> r, phi = cmath.polar ( complex ('1+1j') )
>>> r
1.4142135623730951

>>> phi * 180.0 / math.pi
45.0

Функция rect() модуля cmath делает обратную операцию - она создает комплексное число по значению амплитуды и фазы в полярной системе координат:

>>> cmath.rect (r, phi)
(1.0000000000000002+1j)

Функция phase() из модуля cmath возвращает значение фазы комплексного числа:

>>> cmath.phase ( complex ('1+1j') ) * 180.0 / math.pi
45.0

10. Разное

В Python 3.0 не стало функции xrange(), осталась только range(), но теперь она возвращает не список, а итератор. То есть по сути получается, что старую функцию range() убрали, а функцию xrange() переименовали в range().

Если в Python 2.6 и ранее результатом выполнения скрипта

vals = range (1, 10, 1)
print (vals)
print ( list (vals) )

было

[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

А в результате выполнения кода

vals = xrange (1, 10, 1)
print (vals)
print ( list (vals) )

на экран выводилось

xrange(1, 10)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

То в Python 3.0 после запуска первого скрипта мы увидим

range(1, 10)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

А второй скрипт просто не будет работать.

У словарей в Python 3.0 больше нет метода has_key(), вместо него теперь надо использовать оператор in, поэтому такой код будет работать во всех версиях Python, включая 3.0.

a = dict()
a["1"] = 1
print ("1" in a)

А следующий код в Python 3.0 работать не будет:

a = dict()
a["1"] = 1
print (a.has_key("1") )

Для выявления таких устаревших методов в Python 2.6 введен новый параметр командной строки -3. Если он задан, то транслятор выведет следующее предупреждение, когда будет выполнять третью строку последнего примера:

H:\Черновики\Python 2.6 & 3.0\other_1.py:5: DeprecationWarning: dict.has_key() not supported in 3.x; use the in operator

Кроме метода has_key(), этот параметр укажет транслятору, что надо писать похожие предупреждения, если используются функции apply(), callable(), coerce(), execfile(), reduce(), reload(). Кроме того, предупреждения будут выдаваться при использовании устаревших конструкций языка.

В Python 3.0 разработчики решили отказаться от оператора сравнения <> , вместо него везде нужно будет использовать оператор != . Python 2.6 при использовании оператора <> , если установлен параметр командной строки -3, будет выдавать следующее предупреждение:

DeprecationWarning: <> not supported in 3.x; use !=

В Python 3.0 (но не 2.6) появился новый способ документирования входных параметров и возвращаемого значения функций. После имени каждого параметра функции через двоеточие можно задать любое выражение, которое может быть вычислено на момент описания функции. Это выражение будет связано с именем параметра в специальном словаре. Также после описания всех параметров, но до символа : можно задать выражение (с помощью оператора -> ), которое будет связано с возвращаемым значением. Самым очевидным применением этой возможности является задание текстовых строк, описывающие входные параметры и возвращаемое значение.

Рассмотрим пример:

def foo (x: "First parameter", y: "Second parameter") -> "Summ":
    return x + y

В этом примере с параметром x мы связали значение строкового выражения "First parameter" , с параметром y - "Second parameter" . С выходным значением мы связали символьное выражение "Summ" . В Python 3.0 у всех вызываемых типов теперь есть член __annotations__ , который представляем собой словарь. В качестве ключей в нем выступают строковые выражения с именами параметров, а в качестве значений - связанные с этими параметрами описания (аннотации). Если мы выполним следующий код:

def foo (x: "First parameter", y: "Second parameter") -> "Summ":
    return x + y

print (foo.__annotations__)

то в результате получим:

{'y': 'Second parameter', 'x': 'First parameter', 'return': 'Summ'}

Как видно, возвращаемому значению соответствует ключ 'return' .

Здесь мы везде использовали строковые выражения, но это не обязательно. Например, можно было написать такую аннотацию:

def foo (x: [1, 2, 3, 4], y: 5 + 7) -> None:
    pass

print (foo.__annotations__)

Результатом работы этого скрипта является следующий словарь:

{'y': 12, 'x': [1, 2, 3, 4], 'return': None}

Эта возможность может быть удобна для использовании в системах автоматической генерации документации, чтобы более наглядно описывать параметры функций, или при использовании в различных IDE, чтобы прямо в подсказках показывать пользователю что означает тот или иной параметр.

Документация по Python, начиная с версии 2.6, будет иметь другое оформление, более раскрашенное. Мне новое оформление понравилось, к тому же теперь при описании методов класса пишется класс, к которому принадлежит метод. Некоторые подразделы, в отличие от старой документации, теперь стали располагаться на одной странице. По содержанию в старых разделах ничего не изменилось, описания некоторых классов (например, CDATASection) как были так и остались недоступны через индекс. Ссылка на онлайновую документацию вы можете найти в конце статьи в разделе 12. Ссылки.

В данный момент справка еще не полностью написана (попадаются моменты, где разработчики сделали себе пометки, что какие-то части нужно дописать), но в целом впечатление неплохое.

11. Заключение

В заключении кратко перечислим наиболее значимые изменения, которые произошли в языках Python 2.6 и 3.0.

  • Python 2.6. Появился новый параметр командной строки -3 для предупреждений об использовании конструкций, которые не будут работать в Python 3.0.
  • Python 2.6, 3.0. Появился новый способ форматирования строк с помощью метода format().
  • Python 2.6, 3.0. Появился новый способ создания свойств.
  • Python 2.6, 3.0. Появилась возможность создавать абстрактные классы с абстрактными методами и свойствами.
  • Python 2.6, 3.0. Изменился синтаксис обработки исключений. Классы вызываемых исключений в Python 3.0 должны быть производными от базового класса BaseException.
  • Python 2.6, 3.0. with теперь полноценное ключевое слово.
  • Python 2.6, 3.0. Введены особые числа с плавающей точкой - nan, -inf и +inf.
  • Python 2.6, 3.0. Появились новые функции для работы с комплексными числами.
  • Python 3.0. Все строки теперь используют Unicode, класса unicode больше нет.
  • Python 3.0. print теперь является функцией (в Python 2.6 возможно использование print и как функции, и как ключевого слова).
  • Python 3.0. Разделены операторы дробного ( / ) и целочисленного ( // ) деления.
  • Python 3.0. Функция xrange() заменила функцию range().
  • Python 3.0. Новый способ документирования функций и методов.
  • Python 3.0. Оператора <> больше нет, вместо него != .

Мы рассмотрели основные нововведения языков Python 2.6 и 3.0. Из-за потери обратной совместимости язык Python 3.0 с версиями 2.х, можно назвать новым языком, который будет развиваться некоторое время параллельно версиям 2.х, хотя не думаю, что мы когда-нибудь увидим Python 2.7 с новыми возможностями. Скорее всего в ветке 2.х будут только исправляться ошибки.

UPDATE: Похоже, что на счет Python 2.7 я ошибался, некоторые подробности вы можете прочитать у меня в блоге - Python 2.7?.

Обе версии 2.6 и 3.0 кажутся очень интересными. Если при переходе на версию 3.0 надо будет хорошенько тестировать старый код на работоспособность, то на версию 2.6 переход должен быть практически безболезненным. Осталось дождаться поддержки новых версий Python сторонними библиотеками.

12. Ссылки

Документация по Python 2.6
Документация по Python 3.0

Whats new in Python 2.6
What's new in Python 3.0

Вы можете подписаться на новости сайта через RSS, Группу Вконтакте или Канал в Telegram.
4.5 stars

Рейтинг 4.4/5. Всего 18 голос(а, ов)



ilih 28.06.2008 - 22:21

> Кроме того, в python 2.6 и 3.0 появился новый метод для свойств, так называемый deleter, который вызывается, если к свойству применить функцию del().

метод для удаления свойства и сейчас есть
property( [fget[, fset[, fdel[, doc]]]])
http://docs.python.org/lib/built-in-funcs.html#l2h-57

Jenyay 28.06.2008 - 22:35

ilih

Спасибо, действительно. Сейчас исправлю.

BION 29.06.2008 - 12:29

Спасибо за статью, было интересно почитать!

Olexander Shtepa 29.06.2008 - 13:21

Интерестно. А то лень было читать в оригинале happy smiley.

Dmitry Vasiliev 29.06.2008 - 13:33

Еще одно описание

Кстати, еще год назад написал похожее описание: http://hlabs.spb.ru/development/python/python3000.html

Valery 29.06.2008 - 17:11

confused smileyconfused smileyconfused smileyshrieking smileyangry smiley

Andy 29.06.2008 - 19:14

"КомпЛексные" числа - поправьте опечатку.
А вообщем, авторы гады - такая подстава с делением и строками...

Jenyay 29.06.2008 - 19:46

Andy

> "КомпЛексные" числа - поправьте опечатку

Спасибо, исправил.

mumu 29.06.2008 - 21:32

Спасибо огромное за статью! Было очень интересно почитать

HardNik 29.06.2008 - 23:18

Я думаю, русскоязычным пользователям и, особенно, преподавателям небезинтересна была бы информация о идентификаторах на национальных языках. Тем более после таких бурных дискуссий на dev, и, учитывая, что это решение практически продавленно Гвидо. К сожалению, он же настоял на удалении reduce().

ZioN 29.06.2008 - 23:20

м?


В новых версиях Python появился новый, более удобный способ задания свойств. До Python 2.6 и 3.0 класс со свойствами мог выглядеть примерно так:

    def getx(self):
        print ("*** X.Getter(): x = {0}".format (self._x) )
        return self._x

вроде говорится что до 2.6 и 3.0 и тут используете функцию format :?

Jenyay 30.06.2008 - 09:15

HardNik

> Я думаю, русскоязычным пользователям и, особенно, преподавателям небезинтересна была бы информация о идентификаторах на национальных языках.

Пожалуй, Вы правы, хотя я не сторонник программ, где в качестве идентификаторов используются нелатинские буквы (к комментариям это не относится).

ZioN

> вроде говорится что до 2.6 и 3.0 и тут используете функцию format :?

Мда, наверное немного нелогично, увлекся format'ом :) Попозже перепишу пример с использованием print'а.

mishok13 01.07.2008 - 13:16

А про dictviews, естественно, ни одного упоминания. Забыли также про dict comprehensions, новые модули multiprocessing и json, полную перестройку stdlib, унификацию типов long и int, новый синтаксис описанный в pep-3132, новые функции в itertools и т.д. Чтобы описать все новое в 3.0 одной статьи не хватит.
2HardNik:
reduce перенесли из модуля builtins в модуль functools, никто его не удалял.

Jenyay 01.07.2008 - 21:37

mishok13
Да, это все на вторую часть статьи тянет. Кстати, multiprocessing я видел, но в документации этот модуль еще не описан.

Шепелев Сергей 06.07.2008 - 15:49

TY спасибо за труд

-

PerereresusNeVlezaetBuggy 02.10.2008 - 19:54

Спасибо, одна из немногих толковых статей по Питону (благодаря которой я понял, что не так уж много потерял, избежав тесного общения с этим языком ;) ). :)

Jenyay 02.10.2008 - 21:18

PerereresusNeVlezaetBuggy

Мне сначала питон показался очень, мягко говоря, специфическим языком, но потом на него крепко подсел.

А вообще не ожидал тебя здесь увидеть :))

Dimka 07.10.2008 - 10:11

функции высоко порядка будут возвращать итераторы вместо списков.

Сергей 13.03.2009 - 22:46

По поводу "глюков бета-версии"

Не исключено, что в Вашем фрагменте кода

fp = file ("print_test.txt", "w")
print ("Тест1", "Тест2", "Тест3", file = fp)
fp.close()

интерпретатор "не переваривает" не вторую строку, как Вы считаете, а первую. Возможно, ключевого слова "file" просто не существует. А если заменить Вашу первую строку строкой

fp = open ("print_test.txt", "w")

, то всё будет работать нормально.

Jenyay 13.03.2009 - 23:13

Сергей, похоже, Вы правы. Спасибо, убрал эту фразу :)

 05.05.2009 - 16:32

где скачать!!!???confused smileyconfused smileyconfused smiley

Jenyay 05.05.2009 - 16:36

Здесь - http://python.org/


Подписаться на комментарии
Автор:
Тема:
 Ваш комментарий
 
 
Введите код 721