__toc__
За традицією, я повинен занудити вас фундаментальними елементами програмування, аби ми могли повільно перейти до створення чогось корисного. Давайте все це пропустимо. Ось завершена програма, написана на Python. Вона, мабуть, взагалі не містить для вас жодного сенсу. Не хвилюйтесь через це, тому що ми збираємось розібрати кожен її рядок. Але спробуйте все-таки прочитати і перевірити, наскільки ви її зрозумієте.
Пірнаймо! SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}
def approximate_size(size, a_kilobyte_is_1024_bytes=True): '''Convert a file size to human-readable form.
Keyword arguments: size -- file size in bytes a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024 if False, use multiples of 1000
Returns: string
''' if size < 0: raise ValueError('number must be non-negative')
multiple = 1024 if a_kilobyte_is_1024_bytes else 1000 for suffix in SUFFIXES[multiple]: size /= multiple if size < multiple: return '{0:.1f} {1}'.format(size, suffix)
raise ValueError('number too large')
if __name__ == '__main__': print(approximate_size(1000000000000, False)) print(approximate_size(1000000000000))
Тепер, давайте запустимо її в командному рядку (перед цим збережіть текст в файл на зразок humansize.py). Під Windows це виглядатиме так: c:\home\diveintopython3\examples> c:\python31\python.exe humansize.py 1.0 TB 931.3 GiB
На MacOS та Linux так: you@localhost:~/diveintopython3/examples$ python3 humansize.py 1.0 TB 931.3 GiB
Що щойно відбулось? Ви запустили свою першу Python-програму, викликавши в командному рядку інтерпретатор і передавши йому назву скрипта, який хотіли запустити. Скрипт описує єдину функцію approximate_size(), яка отримує точний розмір файлу в байтах, і повертає "гарний", але приблизний розмір.(Ви могли бачити такий в Windows Explorer, чи Mac OS X Finder, чи Nautilus чи Dolphin чи Thunar на Linux, і в багатьох інших файлових менеджерах. Якщо каталог містить файл TODO розміром 1093 байт, файловий менеджер не виводить TODO 1093 bytes; він показує щось на зразок TODO 1 KB натомість.)
У кінці скрипта ви побачите дві команди - print(approximate_size(arguments)). Це виклики функцій. Один викликає approximate_size з аргументами і передає її результат в функцію print. Функція print - вбудована, ви ніколи не побачите її явного опису. Зате ви можете її використовувати будь-де і будь-коли. (Є ціла купа вбудованих функцій і ще більше функцій, які розділені за модулями. Терпіння...)
То чому запуск скрипта з консолі щоразу дає одні й ті ж результати? Давайте розберемось. І спершу подивимось на функцію approximate_size().
Опис функцій Python, як і більшість інших мов програмування, має функції, але не має окремих заголовкових файлів як C++ чи секції interface/implementation в Pascal. Коли вам необхідна функція, ви просто оголошуєте її, ось так:
def approximate_size(size, a_kilobyte_is_1024_bytes=True):
Оголошення функції починається з ключового слова def, за яким іде ім’я функції, а після неї - параметри в дужках. Якщо параметрів кілька, вони розділяються комами.
Також варто зауважити, що для функції не задається тип, який вона повертає. Функції в Python не фіксують тип даних, який вони повертають. Вони навіть не визначають наперед чи буде взагалі повертатись якесь значення (хоча насправді кожна функція в Python повертає результат. Якщо в ній виконується оператор return - передається значення виразу після нього. Якщо ж після нього нічого не має, або оператор взагалі не використовується, повертається стандартне значення None, тобто "нічого").
Функція approximate_size() отримує два аргументи: size та a_kilobyte_is_1024_bytes, проте для жодного з них не задається тип даних. В Python змінні ніколи явно не отримують тип. Python сам з’ясовує якого типу значення містить змінна.
Необов’язкові та іменовані аргументи Python дозволяє задавати для аргументів типові значення. Якщо значення аргументу не передається при виклику функції, то йому нададуть типового значення. Крім того, аргументи можна передавати в довільному порядку, якщо використовувати іменовані аргументи.
Для прикладу, розглянемо ще раз функцію approximate_size(): def approximate_size(size, a_kilobyte_is_1024_bytes=True):
Другий аргумент a_kilobyte_is_1024_bytes має типове значення True. Це означає, що аргумент необов’язковий: ви можете викликати функцію без нього, а функція буде поводити себе, ніби їй передали True другим аргументом.
Зверніть увагу на кінець скрипту:
if __name__ == '__main__': print(approximate_size(1000000000000, False)) print(approximate_size(1000000000000))
У другому рядку ми викликаємо функцію з двома аргументами. Всередині функції змінна a_kilobyte_is_1024_bytes матиме значення False, тому що ми задали його свідомо. В останньому рядку approximate_size викликається з одним аргументом. Це нормально, бо другий є необов’язковим і всередині функції матиме значення True.
Також можна передавати значення в функцію за іменем аргументу:
>>> from humansize import approximate_size >>> approximate_size(4000, a_kilobyte_is_1024_bytes=False) '4.0 KB' Це викликає функцію approximate_size(), передавши 4000 в якості першого аргумента (size) та False для аргумента a_kilobyte_is_1024_bytes. (Який є другим аргументом випадково, але незабаром ви побачите, що не обов'язково дотримуватись порядку аргументів).
>>> approximate_size(size=4000, a_kilobyte_is_1024_bytes=False) '4.0 KB' Це викликає функцію approximate_size(), передавши 4000 аргументу з іменем size та False для аргументу, названого a_kilobyte_is_1024_bytes.
>>> approximate_size(a_kilobyte_is_1024_bytes=False, size=4000) '4.0 KB' Це викликає функцію approximate_size(), передавши False аргументу з іменем a_kilobyte_is_1024_bytes та 4000 для аргумента size. (Бачите? Я ж казав, що порядок не має значення.)
>>> approximate_size(a_kilobyte_is_1024_bytes=False, 4000) File "<stdin>", line 1 SyntaxError: non-keyword arg after keyword arg Цей виклик не працює, бо ви передали неіменований (позиційний) аргумент після іменованого, а таке ніколи не спрацьовує. Якщо читати список аргументів зліва направо, то після того як ви натрапили на іменований аргумент, всі наступні також мусять бути іменованими.
>>> approximate_size(size=4000, False) File "<stdin>", line 1 SyntaxError: non-keyword arg after keyword arg Цей виклик теж не працює, з тієї ж причини, що й попередній. Це було несподіванкою, правда? Зрештою, ви передали 4000 аргументу з іменем size, тоді "очевидно", що значення False призначалось аргументу a_kilobyte_is_1024_bytes. Але Python так не працює. Як тільки він зустрічає іменований аргумент, він очікує що всі інші після нього теж будуть іменованими.
Написання читабельного коду Не буду знуджувати вас довгою повчальною промовою про важливість документування свого коду. Просто пам’ятайте, що код пишеться раз, а читається багато разів, і найбільш важливою аудиторією читачів є ви самі шість місяців по тому (після того, як ви вже все забули, але мусите щось виправити). Python спрощує написання читабельного коду, і цим варто скористатись. Ви подякуєте мені через шість місяців.
Рядки документації Функції можна документувати, задаючи їм рядки документації (в народі докстрінги). У цій програмі функція approximate_size() має докстрінг:
def approximate_size(size, a_kilobyte_is_1024_bytes=True): '''Convert a file size to human-readable form.
Keyword arguments: size -- file size in bytes a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024 if False, use multiples of 1000
Returns: string
'''
Потрійні лапки задають багаторядковий літерал (текстову константу). Усе між лапками, що відкривають, і лапками, які закривають, входить до літерала, включно з переходами на новий рядок, відступами на початку та іншими лапками. Такі "багаторядкові рядки" (а це саме Python-рядки, а не окремий тип!) можна зустріти всюди, та найчастіше саме в докстрінгах.
Рядок документації, якщо він є, мусить бути першим з того, що описується в функції (тобто йти одразу за оголошенням функції). Формально вам не потрібно задавати докстрінг для функції, проте це завжди бажано. Я знаю, про це казали на всіх уроках програмування, але Python дає додатковий стимул писати документацію: вона доступна під час виконання як атрибут функції.
Шляхи пошуку для import Доки я не зайшов занадто далеко, розповім ще про шляхи пошуку бібліотек. Коли ви робите спробу імпортувати модуль, Python шукає його в кількох місцях. Якщо конкретно - він дивиться у всіх каталогах, перелічених у списку sys.path. Це стандартний список, і ви можете легко його переглядати та модифікувати, використовуючи звичайні методи для роботи зі списками. (Ви дізнаєтесь про це більше у розділі Стандартні типи даних).
>>> import sys
Імпортування модуля sys відкриває нам доступ до його функцій та інших атрибутів.
>>> sys.path ['', '/usr/lib/python31.zip', '/usr/lib/python3.1', '/usr/lib/python3.1/plat-linux2@EXTRAMACHDEPPATH@', '/usr/lib/python3.1/lib-dynload', '/usr/lib/python3.1/dist-packages', '/usr/local/lib/python3.1/dist-packages']
sys.path - це список шляхів до каталогів, у яких Python шукає файли для імпорту. (Він матиме різний вигляд, залежно від операційної системи, версії інтерпретатора й каталогу, до якого його встановили. Python перевірить ці каталоги (в такому ж порядку) на наявність файла з розширенням *.py, ім’я якого збігається з назвою модуля, який ви хочете імпортувати.
>>> sys <module 'sys' (built-in)>
Чесно кажучи, я збрехав. Правда набагато складніша, тому що не всі модулі зберігаються як файли *.py. Деякі є вбудованими прямо в Python. Вбудовані модулі поводяться так само, як і звичайні, тільки їх вихідний Python-код недоступний, бо вони не написані мовою Python!
>>> sys.path.insert(0, '/home/mark/diveintopython3/examples')
Ви можете додати новий каталог до шляхів імпорту під час виконання скрипта, і після цього Python при кожній спробі імпорту буде шукати модулі і в цьому каталозі. Ефект буде зберігатись, доки працює інтерпретатор або доки хтось не видалить каталог зі списку.
>>> sys.path ['/home/mark/diveintopython3/examples', '', '/usr/lib/python31.zip', '/usr/lib/python3.1', '/usr/lib/python3.1/plat-linux2@EXTRAMACHDEPPATH@', '/usr/lib/python3.1/lib-dynload', '/usr/lib/python3.1/dist-packages', '/usr/local/lib/python3.1/dist-packages']
Користуючись sys.path.insert(0, new_path), ви додали новий каталог першим до списку sys.path list, і тому Python буде спершу шукати в ньому. Це майже завжди те, що нам потрібно. У випадку конфліктів імен (наприклад, якщо Python поставляється з однією версією якоїсь бібліотеки, а вам потрібна інша), це гарантує, що Python буде використовувати ваші модулі, а не ті, що постачались з системою.
Все — об’єкт Якщо ви раптом пропустили: я нещодавно сказав, що функції в Python мають атрибути і що ці атрибути доступні під час виконання програми. Функція, як і все інше в Python, є об’єктом.
Запустіть інтерактивну оболонку Python і прямуйте за мною:
>>> import humansize
Цей рядок імпортує нашу програму humansize як модуль - шматок коду, який ви можете використовувати або інтерактивно, або з іншої програми Python. Тільки-но ви імпортуєте модуль, у вас з’явиться можливість звертатись до його публічних функцій, класів та атрибутів. Саме таким способом скрипти отримують доступ до функціональності інших модулів. Ви в інтерактивній оболонці також можете цим користуватись. Це справді важлива властивість Пайтона, і ми розглянемо її детальніше впродовж цієї книжки.
>>> print(humansize.approximate_size(4096, True)) 4.0 KiB
Якщо ви хочете використовувати функції, описані в імпортованих модулях, ви повинні додавати також ім’я модуля. Не достатньо написати просто approximate_size, потрібно humansize.approximate_size. Якщо ви використовували класи в Java, вам це повинно здатись знайомим.
>>> print(humansize.approximate_size.__doc__) Convert a file size to human-readable form.