Перейти до вмісту

Підручник мови Python/Ввід і вивід

Матеріал з Вікіпідручника

Існує кілька способів презентації виводу програми мовою Python; дані можуть виводитися для читання людиною чи записуватися у файл для використання у майбутньому. У цьому розділі буде розглянуто деякі можливості.


Можливості форматування виводу

[ред.]

Раніше ми познайомилися з двома способами виводу значень: твердження з виразами та оператор print. (Третій спосіб — це використання методу write() файлових об'єктів; на файл стандартного виводу можна посилатися через sys.stdout. Див. "Опис бібліотеки" для подальшої інформації з цього приводу).

Часто потрібно більше контролю над форматуанням виводу, аніж простий друк списку значень розділених комами. Існують два способи форматування виводу. Перший — це самому створити рядки для виводу: за допомогою операцій зрізу та з'єднання можна отримати будь-яке можливе форматування. Стандартний модуль string має деякі корисні операції для заповнення рядків до певної ширини колонок, про що йтиметься незабаром. Другий спосіб — використання оператора % з рядком в якості лівого параметра. Оператор % інтерпретує лівий операнд подібно до функції sprintf() і застосовує до нього правий аргумент, повертаючи при цьому рядок, що є результатом операції форматування.

Тоді лишається таке питання: яким чином можна конвертувати різні значення у рядки? На щастя, Пайтон має функції для конвертування будь-яких значень у рядки: repr() та str(). Зворотні лапки (``) еквівалентні функції repr(), але їхне вживання небажане.

Функція str() створює текст який зручно читати людині, тоді як repr() — текст призначений для інтерпритатора. Для об'єктів, що не мають читабельного представлення, str() поверне такий самий текст, як і repr(). Багато значень, скажімо, числа чи такі структури як списки чи словники, мають однакову репрезентацію при застосуванні будь-якої з цих функцій. Рядки та числа з рухомою крапкою мають дві різні репрезентації.

Окремі приклади:

>>> s = 'Hello, world.'
>>> str(s)
'Hello, world.'
>>> repr(s)
"'Hello, world.'"
>>> s1='привіт, світе\n' # кодування UTF-8
>>> str(s1)
'\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd1\x96\xd1\x82, \xd1\x81\xd0\xb2\xd1\x96\xd1\x82\xd0\xb5\n'
>>> repr(s1)
"'\\xd0\\xbf\\xd1\\x80\\xd0\\xb8\\xd0\\xb2\\xd1\\x96\\xd1\\x82, \\xd1\\x81\\xd0\\xb2\\xd1\\x96\\xd1\\x82\\xd0\\xb5\\n'"
>>> print s1
привіт, світе

>>> str(0.1)
'0.1'
>>> repr(0.1)
'0.10000000000000001'
>>> x = 10 * 3.25
>>> y = 200 * 200
>>> s = 'x дорівнює ' + repr(x) + ', а y — ' + repr(y) + '...'
>>> print s
x дорівнює 32.5, а y  40000...
>>> # Функція repr() додає лапки та зворотні косі:
... hello = 'hello, world\n'
>>> hellos = repr(hello)
>>> print hellos
'hello, world\n'
>>> # Аргументом функції repr() може бути будь-який об'єкт:
... repr((x, y, ('spam', 'eggs')))
"(32.5, 40000, ('spam', 'eggs'))"
>>> # зворотні лапки зручні у діалоговому режимі:
... `x, y, ('spam', 'eggs')`
"(32.5, 40000, ('spam', 'eggs'))"

Ось два способи виводу таблиці квадратів та кубів:

>>> for x in range(1, 11):
...     print repr(x).rjust(2), repr(x*x).rjust(3),
...     # Зауважте кому в кінці попереднього рядка
...     print repr(x*x*x).rjust(4)
...
 1 1 1
 2 4 8
 3 9 27
 4 16 64
 5 25 125
 6 36 216
 7 49 343
 8 64 512
 9 81 729
10 100 1000
>>> for x in range(1,11):
...     print '%2d %3d %4d' % (x, x*x, x*x*x)
... 
 1 1 1
 2 4 8
 3 9 27
 4 16 64
 5 25 125
 6 36 216
 7 49 343
 8 64 512
 9 81 729
10 100 1000

(Зауважте, що один пробіл між колонками було додано функцією print, яка завжди друкує пробіли між аргументами).

Цей приклад демонструє метод rjust() рядкових об'єктів, який вирівнює рядки по правій стороні шляхом додання пробілів з лівої сторони. Існують також подібні методи для вирівнювання по центру (center()) та по лівій стороні (ljust()). Ці методи не здіснюють виводу, а лише повертають новий рядок. Якщо рядок, що вирівнюється, задовгий — то вони повертають його як є, без скорочення. При цьому вигляд колонок буде спотворено, але здебільшого це краще, ніж вивід хибного значення. (Якщо скорочення значення все-таки потрібне, то цього можна завжди досягти за допомогою операції зрізу: "x.ljust(n)[:n]").

Існує також інший метод, zfill(), що додає зліва до рядка, що виражає число нулі. Цей метод також розуміє знаки плюс та мінус:

>>> '12'.zfill(5)
'00012'
>>> '-3.14'.zfill(7)
'-003.14'
>>> '3.14159265359'.zfill(5)
'3.14159265359'

Використання оператора % виглядає таким чином:

>>> import math
>>> print 'Пі приблизно дорівнює %5.3f.' % math.pi
Пі приблизно дорівнює 3.142.

Якщо треба сформатувати в рядок більш ніж одне значення, то правий операнд повинен задаватися кортежем, як у цьому прикладі:

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
>>> for name, phone in table.items():
...     print '%-10s ==> %10d' % (name, phone)
... 
Jack ==> 4098
Dcab ==> 7678
Sjoerd ==> 4127

Більшість форматувань працюють точнісінько як і в C і потребують належного типу, але при передачі неправильного типу ви отримуєте виняток, а не фатальну помилку (core dump). Формат %s є найбільш гнучким: якщо відповідний аргумент не є рядком, то відбувається авоматична конверсія за допомогою вбудованої функції str(). Використання зірочки (*) для передачі ширини чи точності як окремого (цілочислового) аргумента також можливе. Ключі форматування мови C %n та %p не підтримуються.

Якщо потрібно сформатувати доволі довгий рядок, який ви не хочете розбивати на окремі підрядки, то набагато простіше посилатися на на параметри за допомогою назв, а не за їх порядком. Це можна зробити за допомогою нотації %(назва)формат, як показано нижче:

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print 'Jack: %(Jack)d; Sjoerd: %(Sjoerd)d; Dcab: %(Dcab)d' % table
Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Це особливо корисно у комбінації з новою вбудованою функцією vars(), яка повертає словник, що складається з усіх локальних змінних.

Зчитування і запис файлів

[ред.]

Функція open() повертає файловий об'єкт і здебільшого вживається з двома аргументами: "open(назва_файлу, режим)".

>>> f=open('/tmp/workfile', 'w')
>>> print f
<open file '/tmp/workfile', mode 'w' at 80a0960>

Перший аргумент — рядок, що є назвою файлу. Другий аргумент - також рядок, що містить кілька символів, котрі описують, як буде використовуватись файл. Режим може бути 'r' (read), коли файл відкривається лише для зчитування, 'w' (write) — для запису (існуючий файл з цією назвою буде зтерто), 'a' (append) — для додання (усі нові дані будуть автоматично додані в кінець файла), а також 'r+', що відкриває файл для зчитування та запису одночасно. Аргумент, що задає режим не є обов'язковим, якщо його не вказано, то файл буде відкрито для зчитування.

На системах Windows та Macintosh 'b', доданий до режиму, вказує на те, що файл повинен бути відкрито у бінарному режимі. Таким чином на цих системах маємо такі додаткові режими: 'rb', 'wb', and 'r+b'. Взаємодія Windows з текстовими та бінарними файлами відрізняється; символ нового рядка автоматично опускається при зчитуванні чи записі даних. Така позакулісна зміна файлових даних годиться для роботи з файлами у кодуванні ASCII, але вона зіпсує бінарні дані таких форматів як JPEG чи EXE. При зчитуванні чи записі таких файлів необхідно використовувати бінарний режим. (Зауважте, що точна семантика текстового режиму на Macintosh залежить від використовуваної бібліотеки мови C).

Методи файлових об'єктів

[ред.]

У цьому розділі припускається, що файловий об'єкт з назвою f вже було створено.

Зчитування змісту файла здійснюється за допомогою виклику f.read(розмір), який зчитує певну кількість даних і повертає їх у формі рядка. розмір - це необов'язковий числовий аргумент. Якщо розмір пропущено або його значення від'ємне, то буде зчитано і повернуто увесь файл; якщо ж файл удвічі більний за пам'ять вашого комп'ютера — то це ваша особиста проблема. Якщо ж розмір вказано, то буде зчитано кількість байтів, що дорівнює або є меншою за вказану кількість. Якщо дойдено до кінця файла, f.read() поверне пустий рядок ("").

>>> f.read()
'Це увесь файл.\n'
>>> f.read()
''

f.readline() зчитує окремий рядок із файла; символ нового рядка (\n) залишається в кінці рядка (він може бути відсутнім лише в отаньому рядку, якщо файл не закінчується цим символом). Завдяки цьому повернене значення є однозначним; якщо f.readline() повертає пустий рядок, то це означає, що досягнуто кінця файла, тоді ж як пустий рядок репрезентовано символом '\n', тобто рядком, що складається з символа переходу на новий рядок.

>>> f.readline()
'Це перший рядок файла.\n'
>>> f.readline()
'Другий рядок файла.\n'
>>> f.readline()
''

f.readlines() повертає список, що складається з усіх рядків файла. Якщо цей метод отримує необов'язковий параметр бажаний_розмір, то буде зчитано вказану кількість байтів, плюс стільки, скільки потрібно, щоб завершити рядок. Це часто використовується для швидкого зчитування великих файлів радок за рядком без завантаження цілого файла в пам'ять комп'ютера. Повертаються лише цілі рядки.

>>> f.readlines()
['Це перший рядок файла.\n', 'Другий рядок файла\n']

f.write(рядок) записує вміст рядка в файл, повертаючи None.

>>> f.write('Це — тест\n')

f.tell() повертає ціле число, що вказує поточну позицію у файлі, виміряну в байтах, починаючи від початку файла. Для зміни позиції файлового об'єкта слід використовувати "f.seek(скільки, звідки)". Позиція обчислюється шляхом додання значення, вираженого параметром скільки, до пункту відліку, що визначається параметром звідки і може мати такі значення: 0 (початок файла), 1 (поточна позиція), 2 (кінець файла). Параметр звідки може бути пропущено, при цьому пошук позиції відбудеться відносно початку файла.

>>> f=open('/tmp/workfile', 'r+')
>>> f.write('0123456789abcdef')
>>> f.seek(5) # Йдемо до 6-го байта у файлі
>>> f.read(1) 
'5'
>>> f.seek(-3, 2) # Йдемо до 3-го байта з кінця
>>> f.read(1)
'd'

Після того, як роботу з файлом закінчено, слід викликати f.close() для його закриття і звільнення системних ресурсів, що використовуються відкритим файлом. Після виклику f.close() спроба використання файлового об'єкта призведе до помилки.

>>> f.close()
>>> f.read()
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
ValueError: I/O operation on closed file

Файлові об'єкти мають такі додаткові методи, як isatty() та truncate(), що використовуються набагато рідше. Див. "Опис бібліотеки" для докладнішої інформації, що стосується файлових об'єктів.


Модуль pickle

[ред.]

Текст може легко записуватися у файл та зчитуватися з нього. Певного зусилля потребують числа, тому що read() повертає лише текст, який треба обробити за допомогою певної функції, скажімо, int(), котра отримує рядок (напр, "123") і повертає його числове значення (123). При записі складніших типів даних (списків, словників чи реалізацій класів) ситуація значно ускладнюється.

Щоб не завдавати користувачам зайвих турбот, пов'язаних зі створенням та налаштуванням коду для збереження складних типів даних, Пайтон має стандартний модуль, що зветься pickle. Це чудовий модуль, що може взяти будь-який об'єкт мови Пайтон (і навіть певні види коду!), і створити його рядкове представлення. Цей процес зветься запаковуванням (pickling, дослівно "маринування"). Реконструкція об'єкта з його рядкового представлення зветься розпаковуванням (unpickling). Між цими двома операціями рядкове представлення об'єкта може бути збережене у файлі чи переслане через мережу до віддаленої машини.

Якщо існує об'єкт x, і файловий об'єкт f, відкритий для запису, то найпростіший спосіб запаковування об'єкта потребує одного-єдиного рядка коду:

pickle.dump(x, f)

Для зворотньої дії, якщо f — файловий об'єкт, відкритий для зчитування:

x = pickle.load(f)

(Існують також можливості для запаковування кількох об'єктів одночасно, для збереження результату не в файлі тощо. Докладнішу інформацію можна отримати у документації модуля pickle)

pickle — це стандартний спосіб зберігання коду та його подальшого використання іншими програмами чи іншим викликом цієї ж програми. Технічний термін для цього явища — персистентний об'єкт. Через те що pickle дуже широко використовується, численні автори, що створюють розширення для Пайтона, перевіряють чи нові типи даних (наприклад, матриці) можуть нормально запаковуватися та розпаковуватися.