Пориньте у Python 3/Ваша перша програма

Не ховай свої турботи у святій тиші. Маєш проблему? Чудово. Возвеселися, занурся і пізнавай.
Високоповажний Генепола Ґунаратана

За традицією, я повинен занудити вас фундаментальними елементами програмування, аби ми могли повільно перейти до створення чогось корисного. Давайте все це пропустимо. Ось завершена програма, написана на 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, тобто "нічого").

В деяких мовах програмування підпрограми поділяються на функції, які повертають результат, та процедури, які просто виконують певні дії. Python не має такого поділу, тому всі функції повертають хоч щось, навіть якщо це щось - нічого (None).

Функція approximate_size() отримує два аргументи: size та a_kilobyte_is_1024_bytes, проте для жодного з них не задається тип даних. В Python змінні ніколи явно не отримують тип. Python сам з’ясовує якого типу значення містить змінна.

В Java та інших статично типізованих мовах потрібно задавати тип значення, що повертається функцією, та типи її аргументів. В 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.

    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

Замість того, щоб викликати функцію, як це зазвичай робиться, ми просимо значення одного з її атрибутів, __doc__.

import в Python схожий на require в Perl. Тільки-но ви імпортуєте модуль module в Pyhton, можна звертатись до його функцій за допомогою ідентифікатора module.function, тільки-но ви завантажите модуль в Perl, можна буде використовувати його функції через ідентифікатор module::function.

Що таке об’єкт? ред.

Усе в Python - об’єкт, і все може мати атрибути та методи. Всі функції мають вбудованй атрибут __doc__, який повертає докстрінг, описаний в коді функції. Модуль sys є об’єктом, який має (серед багатьох інших) атрибут path. І так далі.

Поки що це не відповідає на більш фундаментальне запитання: що таке об’єкт? Різні мови програмування описують "об’єкт" різними способами. У деяких це означає, що всі об’єкти повинні мати атрибути та методи, в інших - що від всіх об’єктів можна успадковувати функціонал. У Python означення вільніше. Деякі об’єкти не мають ані атрибутів, ані методів, але можуть їх мати. Не від всіх об’єктів можна наслідуватись. Але все є об’єктом в тому сенсі, що все може бути присвоєно змінній чи передано як аргумент у функцію.

Ви могли чути термін "об’єкт першого класу" в інших контекстах. У мові Python функції є об’єктами першого класу. Ви можете передати функцію як аргумент іншій функції. Модулі є об’єктами першого класу. Ви можете передати цілий модуль як аргумент функції. Класи є об’єктами першого класу, і кожен екземпляр класу також є об’єктом першого класу.

Я збираюсь повторити це ще раз, у випадку якщо ви пропустили попередні рази: все в Python є об’єктом. Рядки - об’єкти. Списки - об’єкти. Функції - об’єкти. Класи - об’єкти. Екземпляри класів - об’єкти. Навіть модулі - об’єкти.


* * *


Відступи в коді ред.

Функції в Pyton не мають явних операторів begin/end або фігурних дужок для позначення початку й кінця коду функції. Єдиним розділювачем є двокрапка (:) та відступи в коді.

def approximate_size(size, a_kilobyte_is_1024_bytes=True):     # 1
    if size < 0:                                               # 2
        raise ValueError('number must be non-negative')        # 3
                                                               # 4
    multiple = 1024 if a_kilobyte_is_1024_bytes else 1000
    for suffix in SUFFIXES[multiple]:                          # 5
        size /= multiple
        if size < multiple:
            return '{0:.1f} {1}'.format(size, suffix)

    raise ValueError('number too large')
  1. Блоки коду описуються їх відступами. Під терміном "блок коду" я маю на увазі функції, умовні оператори, тіла циклів і так далі. Відступ позначає початок блоку, а прибирання відступу - його закінчення. Явні дужки чи ключові слова не потрібні. Це означає, що невидимі символи є значимими і повинні бути однорідними. У прикладі вище код функції розділено на блоки чотирма пробілами. Це не обов’язково мусять бути чотири пробіли, головне, щоб кількість їх постійно була однакова. Перший рядок без відступу позначає кінець функції.
  2. У Python після оператора if іде блок коду. Якщо умова в ньому набуває істинного значення, виконується блок з відступом, інакше виконується блок, що йде після else (якщо такий присутній). Дужки навколо умови в if необов’язкові.
  3. Цей рядок знаходиться всередині блоку if. Оператор raise запустить виключення (типу ValueError), але тільки при умові size < 0.
  4. Це ще не кінець функції. Порожні рядки не враховуються при розбитті на блоки. Вони можуть зробити функцію більш читабельною, але не відіграють роль розділювачів.
  5. Цикл for також позначає початок блоку. Блоки коду можуть містити як завгодно багато рядків, аби тільки вони мали однаковий відступ. Цей цикл for містить три рядки коду. Іншого спеціального синтаксису для багаторядкових блоків немає. Тільки робіть відступи - і живіть далі.

Після початкових протестів і кількох жалюгідних аналогій з Фортраном, ви примиритесь і побачите переваги такого синтаксису. Однією з основних переваг є те, що всі програми мовою Python виглядають подібно, тому що відступи - це вимога мови, а не рекомендація стилю. Це спрощує читання і розуміння чужого коду.

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


* * *


Виняткові ситуації ред.

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

Що таке виняткова ситуація, або виняток? Зазвичай це помилка, сигнал про те, що щось пішло не так, як планувалось. (Чесно кажучи, не всі винятки є помилками, але поки що не забивайте собі цим голови.) Деякі мови програмування заохочують повертати коди помилок, які потім потрібно перевіряти. Python заохочує створення виключень, які ви можете врахувати і обробити.

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

На відміну від Java, функції в Python не декларують, які виняткові ситуації вони можуть створити. Це повністю ваша турбота - визначити, які ситуації вам доведеться відловлювати.

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

Python використовує блоки try...except для обробки виняткових ситуацій і оператор raise для їх генерації. Java та C++ використовують блоки try...catch для обробки та оператор throw для генерації.

Функція approximate_size() генерує виняток в двох різних випадках: якщо переданий розмір більший, ніж може бути оброблений фунцією, або якщо він від’ємний.

if size < 0:
    raise ValueError('number must be non-negative')

Синтаксис створення винятку досить простий. Використайте команду raise, за якою напишіть назву виключної ситуації і необов’язковий рядок з поясненням того, що сталось. Цей синтаксис нагадує виклик функції. (Насправді, механізм виключних ситуацій реалізовано як набір класів, і цей оператор raise є фактично створенням екземпляра класу ValueError і передачі рядка 'number must be non-negative' його конструктору. Але це ми забігаємо наперед!)

Вам не потрібно обробляти винятки функції, що їх створила. Якщо функція їх не обробляє, винятки передаються функції, яка її викликала, потім функції, що викликала ту функцію, і так далі вгору по стеку. Якщо виняток ніде не обробляється, то програма аварійно завершиться, а Python надрукує "слід" до стандартного потоку виводу помилок і на цьому кінець. Знову ж таки, можливо - це саме те, що вам потрібно. Все залежить від того, що робить ваша програма.

Обробка помилок імпорту ред.

Один з вбудованих в Python типів виключень - ImportError, яке генерується, коли ви намагаєтесь імпортувати модуль, і ця спроба виявляється невдалою. Це може трапитись через низку причин, але найпоширеніша - модуль не знайдений в шляхах для пошуку модуля. Можна використати цей виняток для додавання необов’язкових функцій до своєї програми. Наприклад, бібліотека chardet дозволяє автоматично визначати кодування символів. Можливо, ваша програма захоче використовувати цю бібліотеку якщо вона існує, але виховано не перериватиме роботи, навіть якщо користувач її не встановив. Це можна зробити за допомогою блока try .. except.

try:
    import chardet
except ImportError:
    chardet = None

Пізніше можна перевірити наявність модуля chardet простим умовним оператором:

if chardet:
    # do something
else:
    # continue anyway

Іншим типовим використанням виняткових ситуацій ImportError є випадок, коли дві бібліотеки реалізують однакове API, але використання однієї з них більш бажане (наприклад, вона швидша, або використовує менше пам’яті). Ви можете спробувати імпортувати перший модуль, і якщо не вийде, завантажити інший. Наприклад, розділ про XML розповідає про два модулі, що реалізують спільне API, наване ElementTree. Перший lxml - сторонній модуль, який потрібно скачувати і встановлювати самостійно. Інший xml.etree.ElementTree - повільніший, але є частиною стандартної бібліотеки Python 3.

try:
    from lxml import etree
except ImportError:
    import xml.etree.ElementTree as etree

Після виконання цього блоку try..except ви імпортували якийсь модуль і назвали його etree. Оскільки обидва модулі реалізують однакове API, решта вашого коду не повинна перевіряти, який модуль був імпортованим. І оскільки модуль, що імпортується, завжди називається etree, решта коду не буде перемежовуватись умовними операторами для виклику по-різному названих модулів.


* * *


Незв’язані змінні ред.

Погляньмо ще раз на рядок коду з функції approximate_size():

multiple = 1024 if a_kilobyte_is_1024_bytes else 1000

Ви ніде не оголошували змінну multiple, ви просто присвоюєте їй значення. Це нормально, бо Python дозволяє вам так роботи. Що Python вам ніколи не дозволить - це звертатись до змінної, якій ще не було присвоєно значення. Така спроба спровокує виняткову ситуацію NameError.

>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> x = 1
>>> x
1

Якось ви подякуєте Python-у за це.


* * *


Усе чутливе до регістру ред.

Усі імена в Python чутливі до регістру: імена змінних, імена функцій, імена класів, імена модулів, імена виняткових ситуацій. Якщо ви можете отримати певне значення, встановити його, викликати або імпортувати - отже, воно чутливе до регістру.

>>> an_integer = 1
>>> an_integer
1
>>> AN_INTEGER
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'AN_INTEGER' is not defined
>>> An_Integer
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'An_Integer' is not defined
>>> an_inteGer
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'an_inteGer' is not defined

І так далі...


* * *


Запуск скриптів ред.

Усе в Python - об’єкти

Модулі в Python є об’єктами і мають кілька корисних атрибутів. Один з таких корисних атрибутів можна використати для тестування модуля. За його допомогою до модуля можна додати спеціальний блок, що буде виконуватись лише при запуску інтерпретатора для самого модуля, а не для скрипта, що його імпортує. Подивіться на кілька останніх рядків humansize.py:

if __name__ == '__main__':
    print(approximate_size(1000000000000, False))
    print(approximate_size(1000000000000))

Як і C, Python використовує == для порівняння та = для присвоєння. На відміну від C, Python не дозволяє присвоєння в виразах, тому не має можливості випадково надати змінній значення при порівнянні.

То що ж робить цей if особливим? Ну, модулі - це об’єкти, і всі модулі мають вбудований атрибут __name__. Він залежить від того, як ви використовуєте модуль. Якщо ви імпортуєте його, тоді __name__ - це назва файлу модуля без розширення файлу та шляху до нього.

>>> import humansize
>>> humansize.__name__
'humansize'

Але ви також можете запускати модуль напряму як окрему програму, і в такому випадку __name__ прийме спеціальне стандартне значення: "__main__". Python обчислить умову в if, вона виявиться істинною, і вкладений блок виконається. У нашому випадку він надрукує два значення.

c:\home\diveintopython3> c:\python31\python.exe humansize.py
1.0 TB
931.3 GiB

І це й є наша перша програма!


* * *


Для подальшого читання ред.

Встановлення · Стандартні типи даних