Знакомство с Python 3: Часть 1. Что нового в новой версии

Более ясный синтаксис для лучшего кода

Python 3 - это новая версия мощного языка программирования общего назначения, автором которого является Гвидо ван Россум. Эта версия не имеет обратной совместимости с версиями 2.x, но зато в ней исправлено несколько синтаксических проблем предыдущих версий.

Сезар Отеро, независимый консультант, IBM

Сезар Отеро - независимый консультант, специализирующийся на Java и Python. Сезар имеет ученую степень по электротехнике, а его вторая специализация - математика.



21.04.2009

Python 3, также известный как Python 3000 или Py3K (неофициальное название-каламбур, по аналогии с операционной системой Microsoft® Windows® 2000), - это новая версия мощного языка программирования общего назначения, автором которого является Гвидо ван Россум. Несмотря на множество улучшений языка, новая версия не является обратно совместимой с линейкой версий 2.x. Многие внесенные изменения уже давно ожидались, например:

  • "Настоящее" (а не целочисленное) деление — например, 1/2 возвращает .5.
  • Типы long и int объединены в один тип, значения которого больше не имеют завершающего символа L.
  • True, False и None теперь являются ключевыми словами.

В этой статье — первой из серии статей о Python 3— освещаются следующие темы: новая функция print(), функция input(), изменения в работе с вводом/выводом (I/O), новый тип данных bytes, изменения в строках и их форматировании, и наконец, изменения во встроенном типе данных dict. Эта статья адресована программистам, уже знакомым с Python, которые хотели бы узнать об изменениях в новой версии, но не желают разбираться в длинном списке Предложений по Улучшению Python (Python Enhancement Proposals или PEPs).

Новая функция print()

Так как print теперь является функцией, вам нужно будет привыкать писать print("hello") вместо print "hello". Я знаю, что это болезненно. Все мои знакомые программисты Python, получая при работе в версии 3 ошибку "incorrect syntax", начинают кричать от бешенства. Я знаю, что два лишних символа раздражают и нарушают обратную совместимость. Но у нового варианта есть и преимущества.

Рассмотрим случай, когда вам нужно перенаправить поток стандартного вывода (stdout) в файл журнала. В следующем примере переменной fid присваивается дескриптор файла log.txt, который открывается для дозаписи в конец файла. Затем с помощью print>> строка перенаправляется в файл fid:

>>>fid = open("log.txt", "a")
>>>print>>fid, "log text"

Другой пример - перенаправление стандартного потока ошибок (sys.stderr):

>>>print>>sys.stderr, "an error occurred"

Оба предыдущих примера хороши, но есть лучшее решение. В соответствии с новым синтаксисом теперь в функцию print() можно просто передать аргумент file. Например:

>>>fid = open("log.txt", "a")
>>>print("log.txt", file=fid)

Синтаксис этого кода гораздо чище. Другим преимуществом нового подхода является возможность изменять разделяющие символы и признак конца строки, передавая нужные значения в параметры sep и end соответственно. Например, чтобы изменить разделяющие символы, вы можете использовать код:

>>>print("Foo", "Bar", sep="%")
>>>Foo%Bar

В общем случае новый синтаксис имеет вид:

print([object, ...][, sep=' '][, end='endline_character_here'][, file=redirect_to_here])

Код внутри квадратных скобок ([]) необязателен. По умолчанию вызов print() возвращает символ новой строки (\n).

Переход от raw_input() к input()

В версиях 2.x языка функция raw_input() читает пользовательский ввод из потока стандартного ввода (sys.stdin) и возвращает строку без завершающего символа новой строки. В следующем примере строка считывается из консоли с помощью функции raw_input() и передается в переменную quest.

>>>quest = raw_input("What is your quest? ")
What is your quest? To seek the holy grail.
>>>quest
'To seek the holy grail.'

Другая функция чтения ввода - input() - в Python 2.x ожидает ввода корректного выражения Python, например 3+5.

Изначально предлагалось исключить как input() так и raw_input()из встроенного пространства имен Python и сделать таким образом возможности ввода доступными только после импортирования. Однако такой подход сочли нежелательным с педагогической точки зрения; вместо простого кода:

>>>quest = input("What is your quest?")

пришлось бы писать:

>>>import sys
>>>print("What is your quest?")
>>>quest = sys.stdin.readline()

Последний вариант содержит гораздо больше кода для такой простой операции. Кроме того, его гораздо дольше объяснять новичкам: необходимо рассказать о модулях и их импортировании, о выводе строк и операторе '.'. (Все это подозрительно напоминает язык Java™...) В итоге в Python 3 функция raw_input() переименовали в input(), и теперь, чтобы получить данные со стандартного ввода не нужно ничего предварительно импортировать. Если же вам нужна функциональность, которую в версиях 2.x предоставляла функция input(), используйте выражение eval(input()).


Немного о байтах

Теперь для хранения бинарных данных используется новый тип - байтовая константа и новый класс - bytes. Объект этого класса представляет собой неизменяемую последовательность целых чисел или ASCII-символов. В действительности это просто неизменяемая версия класса bytearray из Python версии 2.5. Байтовая константа - это строка с помещенным перед ней символом b , например, b'byte literal'.В результате вычисления байтовой константы получается новый объект bytes. Объекты типа bytes также можно создавать с помощью конструктора bytes(), который имеет вид:

bytes([initializer[, encoding]])

Например, в коде ниже создается объект bytes:

>>>b = (b'\xc3\x9f\x65\x74\x61')
>>>print(b)
b'\xc3\x83\xc2\x9feta'

Этот код является избыточным, так как объект bytes можно создать простым присваиванием байтовой константы. (Я не рекомендую делать так, как написано, я просто хотел продемонстрировать, что так можно делать.) Если вы хотите использовать кодировку iso-8859-1, можете сделать так:

>>>b = bytes('\xc3\x9f\x65\x74\x61', 'iso-8859-1')
>>>print(b)
b'\xc3\x83\xc2\x9feta'

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

>>>b'hello' b' world'
b'hello world'

Метод bytes() используется для представления как бинарных данных, так и закодированного текста. Для конвертации bytes в str объект bytes необходимо раскодировать (подробнее об этом дальше). Бинарные данные декодируются с помощью метода decode(). Например:

>>>b'\xc3\x9f\x65\x74\x61'.decode()
'ßeta'

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

>>>data = open('dat.txt', 'rb').read() 
>>>print(data) # data - это строка
>>># здесь выводится содержимое файла dat.txt

Строки

В Python 3 появился отдельный строковый тип str, который ведет себя аналогично типу unicode в версиях 2.x. Другими словами, все строки являются unicode-строками. Также теперь в строках допускаются не ASCII-идентификаторы, что очень удобно при работе с текстами нелатинских алфавитов. Например:

>>>césar = ["author", "consultant"]
>>>print(césar)
['author', 'consultant']

В предыдущих версия Python метод repr()конвертировал 8-битовую строку в ASCII. Например:

>>>repr('é')
"'\\xc3\\xa9'"

Теперь он возвращает unicode-строку:

>>>repr('é')
"'é'"

которая, как было сказано ранее, является значением встроенного типа string.

Строковые и байтовые объекты несовместимы друг с другом. Если вы хотите увидеть строковое представление объекта типа byte - используйте его метод decode(). И наоборот, используйте метод encode() объекта string для получения байтового значения этой строки.


Изменения форматирования строк

Многие программисты на Python считали, что встроенный оператор форматирования строк - % был слишком ограниченным, потому что:

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

Этот стиль является недостаточно гибким, поэтому в Python 3 появился новый способ форматирования строк (хотя и оператор %, и модуль string.Template в версии 3 остались). Строковые объекты теперь имеют метод format(), принимающий позиционные и именованные аргументы, которые передаются в поля подстановки. Поля подстановки внутри строки обозначаются фигурными скобками ({}). Элемент, находящийся внутри подстановочного поля, называется просто полем. Вот простой пример:

>>>"I love {0}, {1}, and {2}".format("eggs", "bacon", "sausage")
'I love eggs, bacon, and sausage'

В поля {0}, {1} и {2} подставляются позиционные аргументы eggs, bacon и sausage метода format(). В следующем примере показано, как использовать format()с именоваными аргументами:

>>>"I love {a}, {b}, and {c}".format(a="eggs", b="bacon", c="sausage")
'I love eggs, bacon, and sausage'

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

>>>"I love {0}, {1}, and {param}".format("eggs", "bacon", param="sausage")
'I love eggs, bacon, and sausage'

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

>>>"{{0}}".format("can't see me")
'{0}'

Позиционный параметр can't see me не был напечатан, потому что в строке нет поля, куда бы его можно было вставить. Заметьте, что это не является синтаксической ошибкой.

Новая встроенная функция format()форматирует единичное значение. Например:

>>>print(format(10.0, "7.3g"))
       10

Символ g обозначает обобщенный формат, который печатает числа фиксированной ширины. Первое число перед точкой указывает минимальную ширину, а число после точки задает точность. В этой статье мы не будем приводить полный синтаксис спецификаторов форматирования, но в разделе Ресурсы вы можете найти ссылки с более подробной информацией.


Изменения во встроенном типе dict

Другое важное изменение в версии 3.0 - это удаление методов словаря dict.iterkeys(), dict.itervalues() и dict.iteritems(). Теперь вместо них можно использовать переработанные методы .keys(), .values() и .items(), которые сейчас вместо прежнего списка ключей или значений возвращают легковесные объекты-контейнеры, похожие на множества (sets). Преимущество здесь состоит в возможности применять к ключам и значениям словаря методы класса set без необходимости их копирования. Например:

>>>d = {1:"dead", 2:"parrot"}
>>>print(d.items())
<built-in method items of dict object at 0xb7c2468c>

Замечание:В Python множества (sets) - это неупорядоченные коллекции уникальных элементов.

Здесь мы создали словарь с двумя парами ключ-значение, а затем распечатали значение, возвращенное методом d.items(), которое является объектом, а не списком значений. Мы можем проверить наличие некоторого элемента в этом объекте, как при работе с объектом множества (set):

>>>1 in d # проверка наличия элемента
True

Вот пример обхода (итерирования) элементов объекта dict_values:

>>>for values in d.items():
...     print(values) 
...
dead
parrot

Но если вам действительно нужен список значений, вы всегда можете явно привести возвращаемое значение к типу list. Например:

>>>keys = list(d.keys())
>>>print(keys)
[1,2]

Новая система ввода/вывода

Метаклассы

Согласно Википедии, "метакласс - это класс, экземплярами которого являются другие классы." Я раскрою эту концепцию более подробно во второй части этой серии.

Перед началом детального изучения новых механизмов ввода/вывода необходимо ознакомиться с абстрактными базовыми классами (abstract base classes или ABC). Более подробно эта тема будет освещена во второй части этой серии статей.

ABC - это классы, объекты которых нельзя создать. Чтобы использовать ABC, нужно создать класс наследник ABC и переопределить в нем его абстрактные методы. Признаком того, что метод является абстрактным, служит наличие у него декоратора @abstractmethod. В новой инфраструктуре ABC также имеется декоратор @abstractproperty для определения абстрактных свойств. Чтобы получить доступ к новой инфраструктуре, нужно импортировать модуль abc стандартной библиотеки. В листинге 1 приведен простой пример.

Листинг 1. Простой абстрактный базовый класс
from abc import ABCMeta

class SimpleAbstractClass(metaclass=ABCMeta):
    pass

SimpleAbstractClass.register(list)

assert isinstance([], SimpleAbstractClass)

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

Листинг 2. Абстрактный класс с декоратором и его реализация
from abc import ABCMeta, abstractmethod

class abstract(metaclass=ABCMeta):
    @abstractmethod
    def absMeth(self):
        pass
 
class A(abstract):
    # must implement abstract method
    def absMeth(self):
        return 0

Теперь, когда мы познакомились с концепцией АВС, давайте приступим к изучению новой системы ввода/вывода. В предыдущих версиях Python отсутствовали некоторые экзотичные, но важные функции, например seek(), для некоторых потокоподобных объектов. Потокоподобные объекты - это файлоподобные объекты с методами read() и write()— например, сокеты или файлы. В Python 3 имеется несколько уровней ввода/вывода, основанных на потокоподобных объектах — уровень простого ввода/вывода, уровень буферизованного ввода/вывода и уровень текстового ввода/вывода — каждый из которых реализуется на основе отдельного абстрактного класса.

Как и раньше, вы можете открывать поток с помощью встроенной функции open(fileName), хотя теперь вы также можете вызвать метод io.open(fileName)). В результате возвращается буферизованный текстовый файл, методы read() и readline() которого возвращают строки. (Помните, что все строки в Python 3 являются unicode-строками.) Также вы можете применять буферизованный бинарный режим открытия файла с помощью вызова open(fileName, 'b'). В этом случае метод read() возвращает байты, но вы уже не можете вызвать метод readline().

Синтаксис встроенной функции open() выглядит так:

open(file,mode="r",buffering=None,encoding=None,errors=None,newline=None,closefd=True)

Файл можно открыть в следующих режимах:

  • r: чтение
  • w: запись
  • a: дозапись в конец файла
  • b: бинарный режим
  • t: текстовый режим
  • +: открытие дискового файла для обновления
  • U: универсальный режим новой строки

Режимом по умолчанию является rt (чтение в текстовом режиме).

Именованный аргумент buffering принимает целочисленные значения, которые определяют политику буферизации:

  • 0: выключает буферизацию
  • 1: строковая буферизация
  • > 1: полная буферизация (по умолчанию)

Значение по умолчанию параметра encoding зависит от платформы. Параметр закрытия файлового дескриптора, closefd, может иметь значение True или False. Если оно равно False, то файловый дескриптор сохраняется после закрытия файла. Передача именифайла здесь не сработает, в этом случае closefd надо выставить в True.

Тип объекта, который возвращает функция open(), зависит от выбранного режима. В таблице 1 показаны типы возвращаемых значений.

Таблица 1. Типы возвращаемых значений для различных режимов открытия файла
РежимВозвращаемое значение
Текстовый режимTextIOWrapper
Бинарный режимBufferedReader
Режим бинарной записиBufferedWriter
Режим бинарной дозаписиBufferedWriter
Режим чтения/записиBufferedRandom

Замечание: Под текстовыми режимами здесь понимаются режимы w, r, wt, rt и т.д.

В примере, приведенном в листинге 3, буферизованный бинарный поток открывается для чтения.

Листинг 3. Открытие буферизованного бинарного потока для чтения
>>>import io
>>>f = io.open("hashlib.pyo", "rb")  # открываем для чтения в двоичном режиме
>>>f                                 # f - это объект типа BufferedReader
<io.BufferedReader object at 0xb7c2534c>
>>>f.close()                         # закрываем поток

Объект BufferedReader имеет ряд полезных методов, например isatty, peek, raw, readinto, readline, readlines, seek, seekable, tell, writable, write и writelines, а также другие. Чтобы увидеть полный список методов, запустите функцию dir() с объектом типа BufferedReader в качестве аргумента.


Заключение

Никто не знает, примет ли сообщество Python третью версию языка. Нарушение обратной совместимости означает одновременное сопровождение двух разных версий. Некоторые разработчики могут не пожелать проводить миграцию своих проектов, даже с помощью специально разработанного конвертера из второй в третью версию. Лично я считаю, что переход с Python версии 2 на версию 3 в основном заключается в переучивании нескольких вещей; определенно это не такое серьезное изменение, как переход с Python, скажем, на Java или Perl. Многие изменения, например, "настоящее" деление и изменения в типе dict, уже давно ожидались. Вызов print() гораздо проще вызова System.out.println() в Java, так что можно сравнительно просто переучиться и начать пользоваться преимуществами нового подхода.

В результате чтения комментариев любителей Python в блогах у меня сложилось мнение, что некоторые изменения — такие как нарушение обратной совместимости— удерживают многих от перехода к новой версии. Lambda-функции изначально планировалось удалить, но в итоге они были оставлены в прежнем виде. Для получения полного списка изменений посетите основной сайт разработки Python. Если у вас достаточно энтузиазма для самостоятельного изучения документов PEP, то в них вы найдете более подробную информацию.

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

Ресурсы

Научиться

Получить продукты и технологии

  • Загрузите последнюю версию Python.
  • Разработайте ваш следующий Linux-проект с помощью пробного ПО от IBM, которое можно загрузить непосредственно с сайта developerWorks.

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


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

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=384094
ArticleTitle=Знакомство с Python 3: Часть 1. Что нового в новой версии
publish-date=04212009