Створення програми в Python

Python ua: Вирішення проблем

В попередніх розділах ми дослідили різноманітні частини мови Python і зараз час поглянути як усі ці частини поєднуються разом, розробивши програму, яка робить щось корисне. Ідея полягає в тому, щоб самотужки навчитися писати скрипти на Python.

Вигадуємо собі якусь "проблему" і знаходимо шляхи її вирішення далі в статті!

 

Проблема

Проблема: "Я хочу програму, яка робить резервні копії усіх моїх важливих файлів."

Незважаючи на те, що це проста програма у нас недостатньо інформації для того, щоб почати. Потрібно трохи аналізу. Наприклад, як нам вказати для яких саме файлів потрібно створювати резервні копії? Як вони будуть зберігатися? Де вони будуть зберігатися?

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

  1. Перелік файлів і каталогів для копіювання вказуються як список.
  2. Резервна копія повинна зберігатися в головному каталозі для резервних копій.
  3. Резервні копії створюються у вигляді zip файлів.
  4. Назва zip архіву — поточна дата і час.
  5. Ми скористаємося стандартною командою zip, яка типово доступна в стандартних інсталяціях диcтрибутивів Linux/Unix. Користувачі Windows можуть встановити програму зі сторінки проекту GnuWin32 і додати шлях до програми (C:\Program Files\GnuWin32\bin) до системної змінної PATH. Зауваж, що ти можеш використовувати будь-яку програму для архівації, але тільки в тому випадку, якщо вона підтримує інтерфейс командного рядка.

 

Вирішення

Оскільки проект нашої майбутньої програми більш-менш узгоджено, ми можемо почати писати код, який є втіленням нашого рішення.

#!/usr/bin/python
# Filename: backup_ver1.py

import os
import time

# 1. Файли і каталоги для резервного копіювання вказуються як список
source = ['"C:\\My Documents"', 'C:\\Code']
# Зауваж, що нам довелося використати подвійні лапки всередині рядка для імен з пробілами

# 2. Резервна копія буде зберігатися в головному каталозі для резервних копій
target_dir = 'E:\\Backup' # Не забудь змінити шлях відповідно до твоїх потреб

# 3. Файли зберігаються як архів формату zip
# 4. Назва архіву — це поточна дата і час
target = target_dir + os.sep + time.strftime('%Y%m%d%H%M%S') + '.zip'

# 5. Використовуємо команду zip для архівації файлів
zip_command = "zip -qr {0} {1}".format(target, ' '.join(source))

# Запустити процес резервного копіювання
if os.system(zip_command) == 0:
	print('Дані збережено до ', target)
else:
	print('Процес закінчився НЕВДАЧЕЮ')

Виведе:


$ python backup_ver1.py
Дані збережено до E:\Backup\20080702185040.zip

Тепер ми перейшли до стадії тестування. Де перевіряємо чи програма працює правильно. Якщо вона не поводиться як ми очікували, тоді потрібно відлагодити програму. Іншими словами, усунути помилки (баги).

Якщо вищенаведений код не працює, напиши print(zip_command) якраз перед os.system і запусти програму. Тепер скопіюй і встав, те що було виведено на екран в результаті виконання команди print(zip_command), в командну оболонку Python, і подивися чи код виконується сам по собі. Якщо ні, то перевір керівництво до команди zip. Якщо ж виконання команди було успішним, то тоді перевір код програми.

Як це працює:

Ми скористалися модулями os і time спершу імпортувавши їх. Потім ми створили список source, де вказали для яких папок і каталогів мають бути створені резервні копії. Змінна target_dir містить шлях до каталога де файли будуть збережені. Ім’я архіву, який ми збираємося створити, складається з поточої дати і часу, який ми з’ясовуємо, використовуючи функцію time.strftime(). Кінцевий файл матиме розширння zip і буде зберігатися в каталозі target_dir.

Зверни увагу на змінну os.sep. Вона містить роздільник директорій, який зважаючи на ОС в якій запущено програму, буде різним. Для Linux і Unix це буде символ '/', для Windows символ '\\', Mac OS же використовує ':'. Завдяки використанню цієї змінної наша програма буде працювати на всіх ОС без змін в програмному коді.

Функція time.strftime() приймає рядок певного вигляду (специфікації), такий як ми використали в програмі вище. Символ %Y буде замінений на поточний рік без століття. Символ %m — на номер поточного місяця у вигляд 01, 02, 12 і т.д. Повний список всіх специфікторів можна знайти в help().

Створили ім’я для цільового zip файлу, використовуючи оператор додавання, який конкатенує рядки разом і повертає новий рядок. Потім ми створили змінну рядкового типу zip_command, яка містить команду, яку ми виконаємо. Для того, щоб переконатися, що ця команда працює спробуй виконати її в командній оболонці (Linux термінал чи командний рядок DOS).

Команда zip має певні параметри і опції. -q вказує на те що команда zip має працювати тихо (жодних повідомлень на екран не буде виводитися). -r означає, що програма буде працювати рекурсивно по відношенню до каталогів, тобто вона пройде по всім каталогам і їх підкаталогам а також, файлам які там є. Дві вищевказані опції можна поєднати: -qr. Метод join ми використали для конвертації списку source в рядок.

Потім, ми нарешті запускаємо команду, скориставшись функцією os.system. Вона запускає команду так ніби вона була запущена прямо з ОС, в командному рядку — функція поверне 0, якшо завершилася успішно, інакше, в програму повернеться код помилки.

В залежності від виводу ми виводимо на екран відповідне повідомлення про те була операція успішною чи ні.

Це все, ми вже створили скрипт для створення резервних копій важливих файлів!

Зауваження для користувачів Windows

Замість двох зворотніх скісних ліній, для екранування керуючих послідовностей, можна використовувати необроблювані рядки. Наприклад, замість 'C:\\Documents' можна r'C:\Documents'. Однак, не використовуй 'C:\Documents', оскільки це зведеться до того що, в тебе буде невідома керуюча послідовність \D.

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

Програма, яку ми написали працює як слід, але (зазвичай) перші програми не працюють так як ти очікуєш. Наприклад, можуть бути проблеми, якщо ти не спланував програму правильно або зробив помилку при наборі коду програми або інше. Відповідно ти будеш змушений повернутися до фази проектування або муситимеш виловлювати помилки в коді.

 

Друга версія

1-а версія нашого скрипта працює. Тим не менше, ми можемо провести деякі вдосконалення, щоб програма працювала краще. Це називається фазою супроводу програми.

Одне із вдосконалень, яке як мені здається, буде корисним це кращий механізм іменування файлів — використовуючи час як ім’я файлу всередині каталога і поточну дату для іменування каталогу всередині головного резервного каталогу. 1-а перевага полягає в тому що резервні копії будуть зберігатися в ієрархічному порядку і таким чином, орієнтуватися у всіх цих файлах буде набагато легше. Наступна перевага — імена файлів стануть набагато коротшими. Третя перевага — легко перевірити чи є бекап для кожного дня.

#!/usr/bin/python
# Filename: backup_ver2.py

import os
import time

# 1. Каталоги і файли для яких треба створити резервні копії перераховуються в
списку
source = ['"C:\\My Documents"', 'C:\\Code']
# Зауваж, ми змушені були викор. подвійні лапки всередині рядка для імені з пробілами

# 2. Копія має зберігатися в головному каталозі
target_dir = 'E:\\Backup' # Не забудь змінити шлях на власний

# 3. Файли зберігаються в zip архіві
# 4. Поточний день — назва для підкаталогу в головному каталозі

today = target_dir + os.sep + time.strftime('%Y%m%d')
# Поточний час — назва для zip архіву
now = time.strftime('%H%M%S')

# Створити підкаталог, якщо його ще не існує
if not os.path.exists(today):
    os.mkdir(today) # створити каталог
    print('Каталог успішно створено', today)

# Ім’я zip файлу
target = today + os.sep + now + '.zip'

# 5. Використовуємо команду zip для створення архіву
zip_command = "zip -qr {0} {1}".format(target, ' '.join(source))

# Запустити процес
if os.system(zip_command) == 0:
    print('Дані збережено до', target)
else:
    print('Процес закінчився невдачею')

Виведе:


$ python backup_ver2.py
Каталог успішно створено E:\Backup\20080702
Дані збережено до E:\Backup\20080702\202311.zip

$ python backup_ver2.py
Дані збережено до E:\Backup\20080702\202325.zip

Як це працює:

Більша частина коду залишилася незмінною. Зміни полягають в перевірці існування каталогу з поточним днем в якості назви всередині головного каталогу. З цією метою ми використали функцію os.path.exists. Якщо каталогу з таким іменем не існує, то ми його створюємо функцією os.mkdir.

 

Третя версія

Друга версія програми працює добре, коли я створюю багато резервних копій. Але, коли їх стає дуже багато, мені стає важко відрізнити які з них для чого були створені. Наприклад, я зробив якісь значні зміни в програмі або презентації, тому хочу вказати, що це були за зміни в імені zip архіву. Цього легко досягти, додавши коментарі користувача до назви архіву.

Зауваження

Наступний код не працює, тому не лякайся, просто роби все як написано тому що це буде уроком для тебе.

#!/usr/bin/python
# Filename: backup_ver3.py

import os
import time


# 1. Каталоги і файли для яких треба створити резервні копії перераховуються в
списку
source = ['"C:\\My Documents"', 'C:\\Code']
# Зауваж, ми змушені були викор. подвійні лапки всередині рядка для імен з пробілами

# 2. Копія має зберігатися в головному каталозі
target_dir = 'E:\\Backup' # Не забудь змінити шлях на власний

# 3. Файли зберігаються в zip архіві
# 4. Поточний день — назва для підкаталогу в головному каталозі

today = target_dir + os.sep + time.strftime('%Y%m%d')
# Поточний час — назва для zip архіву
now = time.strftime('%H%M%S')

# Прийняти коментар від користувача для створення імені архіву
comment = input('Введи коментар --> ')
if len(comment) == 0: # перевіряємо чи коментар було введено
    target = today + os.sep + now  + '.zip'
else:
    target = today + os.sep + now + '_' + 
        comment.replace(' ', '_') + '.zip'

# Створити підкаталог, якщо його ще не існує
if not os.
path.exists(today):
    os.mkdir(today) # створити каталог
    print('Каталог успішно створено', today)

# 5. Використовуємо команду zip для створення архіву
zip_command = "zip -qr {0} {1}".format(target, ' '.join(source))

# Запустити процес
if os.system(zip_command) == 0:
    print('Дані збережено до', target)
else:
    print('Процес закінчився НЕВДАЧЕЮ')

Виведе:


$ python backup_ver3.py
    File "backup_ver3.py", line 25
        target = today + os.sep + now + '_' +
                                            ^
SyntaxError: invalid syntax

Як це (не працює) працює:

Ця програма не працює! Python говорить про синтаксичну помилку, що значить, що структура скрипта не задовольняє Python. Повідомлення про помилку, крім всього іншого, також вказує на місце де саме вона сталася. Отже, ми починаємо відлагоджувати (debugging) нашу програму з вказаного рядка.

Уважно придивившись до коду, ми побачимо, що логічний рядок розташований на двох фізичних, але ми не вказали, що ці два фізичних рядки одна інструкція. В тому місці Python знайшов оператор + без операнду і тому не знає, що робити далі. Згадай, що ми можемо вказати, що логічний рядок продовжується на наступному фізичному за допомогою оберненої скісної риски (\) розташованої наприкінці фізичного рядка. Отже, ми внесли поправки до коду. Корекція коду, у випадку знаходження помилок, називається виправленням помилок.

 

Четверта версія


#!/usr/bin/python # Filename: backup_ver4 import os import time # 1. Каталоги і файли для яких треба створити резервні копії перераховуються в списку source = ['"C:\\My Documents"', 'C:\\Code'] # Зауваж, ми змушені були викор. подвійні лапки всередині рядка для імен з пробілами # 2. Копія має зберігатися в головному каталозі target_dir = 'E:\\Backup' # Не забуть змінити шлях на власний # 3. Файли зберігаються в zip архіві # 4. Поточний день — назва для підкаталогу в головному каталозі today = target_dir + os.sep + time.strftime('%Y%m%d') # Поточний час — назва для zip архіву now = time.strftime('%H%M%S') # Прийняти коментар від користувача для створення імені архіву comment = input('Введи коментар --> ') if len(comment) == 0: # перевіряємо чи коментар було введено target = today + os.sep + now + '.zip' else: target = today + os.sep + now + '_' + \ comment.replace(' ', '_') + '.zip' # Створити підкаталог, якщо його ще не існує if not os.path.exists(today): os.mkdir(today) # створити каталог print('Каталог успішно створено', today) # 5. Використовуємо команду zip для створення архіву zip_command = "zip -qr {0} {1}".format(target, ' '.join(source)) # Запустити процес if os.system(zip_command) == 0: print('Дані збережено до', target) else: print('Процес закінчився НЕВДАЧЕЮ')

Виведе:


$ python backup_ver4.py
Введи коментар --> added new examples
Дані збережено до
E:\Backup\20080702\202836_added_new_examples.zip

$ python backup_ver4.py
Введи коментар --> 
Дані збережено до E:\Backup\20080702\202839.zip

Як це працює:

Тепер програма працює! Давай пройдемося по вдосконаленнях які ми з тобою зробили в 3-й версії. Коментарі від користувача ми отримуємо за доп. функції input. Потім перевіряємо чи він ввів щось шляхом перевірки довжини рядка який він ввів (функція len). Якщо ж він просто натиснув клавішу Enter, не вводячи жодного тексту (можливо це була звичайна копія, без жодних особливих змін), тоді продовжуємо як і раніше.

Однак, якщо коментар було введено, тоді він стає частиною імені zip архіву. Зауваж, що ми замінюємо пробіли символами нижнього підкреслювання — це тому що керувати файлами без пробілів в іменах набагато легше.

 

Більше змін

4-а версія працює задовільно для більшості користувачів, але завжди є простір для вдосконалень. Наприклад, можна вказувати рівень інформативності (через опції -v в CLI) для того, щоб зробити програму більш балакучою (будуть виводитися повідомлення якi вказують на те, що програма робить).

Інше можливе поліпшення — можливість вказати в командному рядку додаткові каталоги і файли для резервного копіювання. Можна отримувати їхні назви зі списку sys.argv, потім додавати до змінної source використовуючи метод extend класу list.

Найбільш важливим поліпшенням програми було б використання модуля zipfile або tarfile замість методу з os.system. Ці модулі є частиною стандартної бібліотеки і ми могли б позбутися від зовнішньої залежності у вигляді програми zip.

Проте я використав метод з os.system чисто з педагогічних міркувань — приклад вийшов достатньо простим для розуміння і в той же час реалістичним.

А ти можеш написати п’яту версію, яка б використовувала модуль zipfile () замість виклику os.system?

 

Процес розробки програмного забезпечення

До цього моменту ми пройшли через різні фази в процесі написання програм. Ці фази можна підсумувати у вигляді наступного списку:

  1. Що (Аналіз)
  2. Як (Планування, проектування)
  3. Зроби це (Втілення в коді, написання програми)
  4. Тест (Тестування і виправлення помилок)
  5. Використання (Розгортання)
  6. Супровід (Вдосконалення)

Рекомендований спосіб написання програм — це процедура, якій ми слідували при створенні скрипта для резервного копіювання: спершу провели аналіз і спроектували майбутню програму, потім почали писати програму спершу, створивши просту її версію, далі провели тестування і виправлення помилок. Переконалися, що програма працює як потрібно. В кінці додали нові можливості. Якщо ти хочеш допрацьовувати програму, то тобі потрібно буде повторювати цей цикл — напиши, протестуй, спробуй так багато раз, як буде потрібно.

 

Підсумок

Ми вже побачили, як створити власну програму/скрипт і різноматні етапи даного процесу.

Далі ми поговоримо про об’єктно-орієнтоване програмування.

 

Джерело перекладу - книга "Byte of Phython".

Переклав Юрченко Дмитро