__toc__
Кожна мова програмування має щось, що могло б бути складним, але в ній воно спеціально зроблено просто. Якщо ви прийшли від іншої мови, ви можете це запросто пропустити, через те, що ваша стара мова не робила це просто (тому що вона натомість робила простим щось інше). Цей розділ розповість вам про спискові вирази, словникові вирази, і множинні вирази: три пов’язані ідеї, які зібрані навколо одної дуже потужної техніки. Але спершу, я хочу здійснити невелику екскурсію, в два модулі, які допоможуть вам переміщуватись по файловій системі.
Робота з файлами та каталогами Python 3 поширюється з модулем os, що містить купу функцій для отримання інформації і у деяких випадках для маніпуляції, локальними каталогами, файлами, процесами та змінними середовища. Python робить все що може, щоб надати уніфіковане API на всіх підтримуваних операційних системах щоб ваші програми могли працювати на будь-якому комп’ютері, з якомога меншою кількістю платформо-залежного коду.
Поточна робоча директорія Якщо ви тільки-но встановили Python, ви будете проводити багато часу в інтерактивній оболонці. Протягом цієї книжки, ви побачити приклади, які працюють за такою схемою:
Імпортують один з прикладів з каталогу examples. Викликають функцію з того модуля Пояснюють результат
Якщо ви не знаєте свою поточну робочу директорію, перший крок напевне буде невдалим, і закінчиться з ImportError. Чому? Бо Python буде шукати модуль в шляхах пошуку імпортів, але не знайде, бо там немає каталогу examples. Щоб це виправити можна зробити дві речі:
Додати каталог examples в шляхи імпорту. Змінити поточну робочу директорію на examples
Поточна робоча директорія - невидима змінна яку Python ввесь час зберігає в пам’яті. Поточна робоча директорія завжди задана, незалежно чи ви в інтерактивній оболонці, запустили скрипт з командного рядка, чи виконуєте CGI скрипт на якомусь веб-сервері.
Модуль os містить дві функції для роботи з поточною робочою директорією.
>>> import os
Він поширюється разом з Python, і його можна імпортувати будь-де в будь-який момент.
>>> print(os.getcwd()) C:\Python31
Щоб визначити поточну робочу директорію використайте функцію os.getcwd(). Коли ви запустаєте графічну оболонку інтерпретатора - поточна директорія зазвичай та, де знаходиться виконуваний файл ітерпретатора. На Windows це зазвиай там, куди ви встановили Python, за замовучуванням C:\Python31. Якщо ви запускаєте інтерпретатор з командної оболонки, то поточна робоча директорія - та сама що й директорія в якій ви були, коли запустили python3.
>>> os.chdir('/Users/pilgrim/diveintopython3/examples')
Використовуйте функцію os.chdir() щоб змінити поточну робочу директорію.
>>> print(os.getcwd()) C:\Users\pilgrim\diveintopython3\examples
Коли я викликав функцію os.chdir(), я використав Лінукс-синтаксис запису шляху (прямі слеші, відсутні буквенні позначення логічних дисків) навіть незважаючи на те, що я в цей момент працюю на Windows. Це одне з місць в яких Python намагається стерти різницю між операційними системами.
Робота з іменами файлів та директорій Поки ми говоримо про директорії, я хочу вказати на те, що модуль os.path містить функції для маніпулювання іменами файлів та директорій.
>>> import os >>> print(os.path.join('/Users/pilgrim/diveintopython3/examples/', 'humansize.py')) /Users/pilgrim/diveintopython3/examples/humansize.py
Функція os.path.join() складає шлях з кількох часткових шляхів. В даному випадку - просто послідовно з’єднує рядки.
>>> print(os.path.join('/Users/pilgrim/diveintopython3/examples', 'humansize.py')) /Users/pilgrim/diveintopython3/examples\humansize.py
В цьому дещо менш тривіальному випадку, виклик os.path.join() додає додатковий слеш до імені перед його приєднанням до імені файла. Це зворотній, а не прямий слеш, тому що я пишу цей приклад під Windows. Якщо ви повторите такі ж дії в Linux чи Mac OS X, ви побачите замість цього прямий слеш. Не жартуйте зі слешами, завжди використовуйте os.path.join() і дозвольте мові Python зробити все правильно.
>>> print(os.path.expanduser('~')) c:\Users\pilgrim
Функція os.path.expanduser() розгортає шлях який використовує ~ для позначення домашнього каталога корисувача в повний. Вона працює на будь-якій платформі де користувачі можуть мати домашню категорію, включно з Linux, Mac OS X та Windows. Шлях який повертається не містить останнього слеша, але функція os.path.join() не звертає на таке увагу.
>>> print(os.path.join(os.path.expanduser('~'), 'diveintopython3', 'examples', 'humansize.py')) c:\Users\pilgrim\diveintopython3\examples\humansize.py
Комбінуючи ці техніки, можна просто конструювати шляхи для директорій та файлів в домашньому каталозі. Функція os.path.join() приймає будь-яку кількість аргументів. Я дуже радів, коли відкрив це, тому що addSlashIfNecessary() - одна з тих дурних маленьких функцій які я завжди писав, коли створював свій інструментарій в новій мові. Не пишіть такі малі дурні функції в Python - про це вже замість вас подбали розумні люди.
os.path також містить функції для поділу повних імен файлів та каталогів на їх складові частини.
>>> pathname = '/Users/pilgrim/diveintopython3/examples/humansize.py' >>> os.path.split(pathname) ('/Users/pilgrim/diveintopython3/examples', 'humansize.py')
Функція split() розділяє повне ім'я файла, і повертає кортеж що містить шлях та назву.
>>> (dirname, filename) = os.path.split(pathname)
Пам'ятаєте, коли я казав що можна використовувати множинне присвоювання щоб отримувати кілька значень з функції? os.path.split() - саме така функція. Ми присвоюємо результат її роботи кортежу з двох змінних. Кожна змінна приймає значення відповідного елемента кортежу що повертається.
>>> dirname '/Users/pilgrim/diveintopython3/examples'
Перша змінна, dirname, приймає значення першого елементу результату os.path.split() - шлях до файла.
>>> filename 'humansize.py'
Друга змінна, filename приймає значення другого елементу кортежу - ім'я файла.
>>> (shortname, extension) = os.path.splitext(filename) >>> shortname 'humansize' >>> extension '.py'
Модуль os.path також містить функцію os.path.splitext(), яка відділяє від імені файла його розширення, і повертає кортеж з двох елементів. Ви можете використати подібну техніку щоб присвоїти ці значення різним змінним.
Лістинги директорій
Модуль glob - інший інструмент зі стандартної бібліотеки. Це простий спосіб отримувати вміст директорій в своїх програмах, і він використовує шаблони з якими ви вже могли бути знайомими працюючи з командного рядка.
>>> os.chdir('/Users/pilgrim/diveintopython3/') >>> import glob >>> glob.glob('examples/*.xml') ['examples\\feed-broken.xml', 'examples\\feed-ns0.xml', 'examples\\feed.xml']
Модуль glob отримує шаблон, і повертає шляхи до всіх файлів і директорій які цьому шаблону відповідають. Наприклад, шаблон "*.xml", відповідає будь-якому файла з розширенням .xml.
>>> os.chdir('examples/')
Тепер перейдемо в підкаталог examples функція os.chdir() може приймати відносні шляхи.
>>> glob.glob('*test*.py') ['alphameticstest.py', 'pluraltest1.py', 'pluraltest2.py', 'pluraltest3.py', 'pluraltest4.py', 'pluraltest5.py', 'pluraltest6.py', 'romantest1.py', 'romantest10.py', 'romantest2.py', 'romantest3.py', 'romantest4.py', 'romantest5.py', 'romantest6.py', 'romantest7.py', 'romantest8.py', 'romantest9.py']
В шаблон можна додавати кілька загальних символів. НАприклад в цьому прикладі шаблон шукає всі файли, назви яких закінчуються розширенням .py і містять слово test де-небудь.
Отримання метаданих файла Кожна сучасна файлова система зберігає певні метадані про кожен файл: час створення, час останньї зміни, розмір і т.п. Python надає єдине API для доступу до цих метаданих. Вам не потрібно відкривати файл, все що потрібно, це назва файла.
>>> import os >>> print(os.getcwd()) c:\Users\pilgrim\diveintopython3\examples
Поточна робоча директорія - examples.
>>> metadata = os.stat('feed.xml')
feed.xml - файл в каталозі examples. Виклик функції os.stat() повертає об'єкт, який містить кілька різних видів відомостей про файл.
>>> metadata.st_mtime 1247520344.9537716
st_mtime - час модифікації, але записаний в не надто зручному форматі. (Технічно це кількість секунд від початку Епохи, яка почалася першого січня 1970-того року. Серйозно.)
>>> import time
Модуль time - частина стандартної бібліотеки. Він містить функції для перетворень між різними форматами часу, настройки часових зон, та форматування часу в тексті.
>>> time.localtime(metadata.st_mtime) time.struct_time(tm_year=2009, tm_mon=7, tm_mday=13, tm_hour=17, tm_min=25, tm_sec=44, tm_wday=0, tm_yday=194, tm_isdst=1)
Функція time.localtime() перетворює часове значення часу з секунд-від-початку-епохи (які є властивістю файла st_mtime значення якої повернула нам функція os.stat()) в більш зручну структуру з року, місяця, дня, години, хвилини, секунд, і так далі.
>>> metadata.st_size 3070
Функція os.stat() також повертає розмір файла в атрибуті st_size. Файл feed.xml містить 3070 байт.
>>> import humansize >>> humansize.approximate_size(metadata.st_size) '3.0 KiB'
Ми можемо передати атрибут st_size функції approximate_size().
Отримання абсолютних шляхів У попередній секції функція glob.glob() повернула список відносних шляхів. Перший приклад використовував шляхи вигляду 'examples\feed.xml', а другий - навіть коротші відносні шляхи що складались лише з імені файла. Поки ви залишаєтесь в тій самій поточній директорії, ці відносні шляхи будуть працювати для відкриття файлів та отримання їх метаданих. Але якщо вам потрібно отримати абсолютні шляхи (тобто такі що включають всі директорії аж до кореневої, або до літери що позначає диск) - тоді вам потрібна функція os.path.realpath().
>>> import os >>> print(os.getcwd()) c:\Users\pilgrim\diveintopython3\examples >>> print(os.path.realpath('feed.xml')) c:\Users\pilgrim\diveintopython3\examples\feed.xml
Спискові вирази
Спискові вирази () - це компактний спосіб створення списку з іншої послідовності, застосовуючи певну функцію до кожного елемента послідовності. (Іноді українською їх ще називали генераторні списки, але цей термін невдалий, тому що генератори, і генераторні вирази - зовсім інша історія).
>>> a_list = [1, 9, 8, 4] >>> [elem * 2 for elem in a_list] [2, 18, 16, 8]
Щоб зрозуміти що твориться прочитайте вираз справа наліво. a_list - список з якого ми беремо елементи. Інтерпретатор бере кожний елемент по черзі, і тимчасово присвоює його значення змінній elem. Після цього обчислює значення функції elem * 2 і додає результат до списку який повертається.
>>> a_list [1, 9, 8, 4]
Спискові вирази створюють нові списки, старі залишаються незмінними.
>>> a_list = [elem * 2 for elem in a_list] >>> a_list [2, 18, 16, 8]
Крім того, можна цілком безпечно присвоювати результат спискового виразу змінній яка використовується у виразі. Python спочатку створить новий список в пам’яті, і тільки після завершення обчислення його елементів присвоїть отримані значення оригінальній змінній.
Всередині спискових виразів можна використовувати довільні вирази мови Python, в тому числі і функції модуля os.
>>> import os, glob >>> glob.glob('*.xml') ['feed-broken.xml', 'feed-ns0.xml', 'feed.xml']
Це поверне список всіх .xml в поточній робочій директорії.
>>> [os.path.realpath(f) for f in glob.glob('*.xml')] ['c:\\Users\\pilgrim\\diveintopython3\\examples\\feed-broken.xml', 'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed-ns0.xml', 'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed.xml']
А цей списковий вираз перетворить список імен файлів на список повних шляхів до них.
Спискові вирази також можуть фільтрувати елементи, включаючи в кінцевий список не кожні з них.
>>> import os, glob >>> [f for f in glob.glob('*.py') if os.stat(f).st_size > 6000] ['pluraltest6.py', 'romantest10.py', 'romantest6.py', 'romantest7.py', 'romantest8.py', 'romantest9.py']