Підручник мови Python/Контрольні структури

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

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

Оператор if[ред.]

Чи не найвідомішим твердженням є if. Приклад:

>>> x = int(raw_input("Прошу ввести ціле число: "))
>>> if x < 0:
...     x = 0
...     print "Від'ємне число замінено на нуль"
... elif x == 0:
...     print 'Нуль'
... elif x == 1:
...     print 'Один'
... else:
...     print 'Більше'
...

Ця конструкція може мати нуль або більше частин elif, частина else — необов'язкова. Ключове слово elif — це скорочення від "else if" ("інакше якщо"), його стисла форма запобігає надмірному виділенню пробілами. Послідовність if ... elif ... elif ... є відповідником операторів switch та case, що зустрічаються в інших мовах програмування.

Оператор for[ред.]

Оператор for у Пайтоні трохи відрізняється від того, до якого ви могли звикнути, використовуючи C або Pascal. Замість постійного перебору чисел арифметичної прогресії (як у Pascal) чи надання користувачеві можливості визначати як крок перебору (ітерації), так і кінцеву умову (як у C), оператор for у мові Пайтон перебирає члени будь-якої послідовності (списку чи рядка) у порядку їхнього в ній розташування. Наприклад:

>>> # Довжини рядків:
... a = ['кіт', 'вікно', 'жбурляти']
>>> for x in a:
...     print x, len(x)
... 
кіт 3
вікно 5
жбурляти 8

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

>>> for x in a[:]: # зробити копію цілого списку за допомогою зрізів
...     if len(x) &gt; 6: a.insert(0, x)
... 
>>> a
['жбурляти', 'кіт', 'вікно', 'жбурляти']

Функція range()[ред.]

Якщо потрібно перебрати послідовність чисел, то тут стане в нагоді стандартна функція range(). Вона створює списки, що містять арифметичні прогресії:

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Задане останнє значення ніколи не є частиною створеного списку: range(10) створює список із десяти елементів, які відповідають індексам послідовності довжиною 10 елементів. Можливо також задати початок списку іншим числом та вказати інший крок (навіть від'ємний):

>>> range(5, 10)
[5, 6, 7, 8, 9]
>>> range(0, 10, 3)
[0, 3, 6, 9]
>>> range(-10, -100, -30)
[-10, -40, -70]

Щоб перебрати індекси послідовності, слід використовувати функції range() та len() таким чином:

>>> a = ['У', 'Марічки', 'є', 'ягнятко']
>>> for i in range(len(a)):
...     print i, a[i]
... 
0 У
1 Марічки
2 є
3 ягнятко

Оператор break та continue; конструкція else у циклах[ред.]

Оператор break, як і в C, перериває найближчий цикл for чи while.

Твердження continue, також запозичене з C, продовжує перебір з наступного кроку.

Циклічні оператори можуть також мати конструкцію else, яка виконується, коли цикл завершується виснаженням списку (для for) або коли умова перестає бути істинною (для while), але не тоді, коли цикл закінчується примусово, за допомогою break. Наведений нижче код показує, як це діє на прикладі пошуку простих чисел:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print n, 'дорівнює', x, '*', n/x
...             break
...     else:
...     # ми потрапили сюди, бо не знайшли спільного множника
...     print n, 'просте число'
... 
2 просте число
3 просте число
4 дорівнює 2 * 2
5 просте число
6 дорівнює 2 * 3
7 просте число
8 дорівнює 2 * 4
9 дорівнює 3 * 3

Оператор pass[ред.]

Оператор pass не робить нічого. Він використовується тоді, коли хоч якась інструкція необхідна синтаксично, але програма не потребує жодної дії. Наприклад:

>>> while True:
...     pass # Очікування сигналу переривання з клавіатури
...

Визначення функцій[ред.]

Створімо функцію, що виводить числа Фібоначчі до певної межі:

>>> def fib(n): # вивести числа Фібоначчі до n
...     """Вивести числа Фібоначчі до n."""
...     a, b = 0, 1
...     while b &lt; n:
...         print b,
...         a, b = b, a+b
... 
>>> # Тепер викликаємо щойно задану функцію:
... fib(2000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

Ключове слово def вводить визначення (definition) функції. За ним повинна бути назва функції та оточений дужками список формальних параметрів. Інструкції які утворюють тіло функції починаються з наступного рядка і повинні бути виділені пробілами. Першим, але необов'язковим, рядком функції може бути символьна константа, яка коротко описує функцію. Її називають рядком документації.

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

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

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

Визначення функції додає назву функції до поточного простору імен. Значення назви функції належить до типу, який ідентифікується інтерпретатором як задана користувачем функція. Це значення може присвоюватися іншій змінній, що потім теж може використовуватися як функція. Тут показано, як діє загальний механізм перейменування:

>>> fib
&lt;function object at 10042ed0&gt;
>>> f = fib
>>> f(100)
1 1 2 3 5 8 13 21 34 55 89

Можливо хтось скаже що fib — не функція, а процедура. У Пайтоні, як і в C, процедури — це функції, що не повертають жодного значення. Власне, з технічної точки зору, процедури таки повертають певне значення, хоча й досить нецікаве. Це значення None. Зазвичай, це значення не виводиться інтерпретатором, якщо це єдине можливе значення для виводу. Але якщо дійсно хочеться його побачити, його потрібно роздрукувати явно:

>>> print fib(0)
None

Створення функції, що повертає список чисел Фібоначчі замість виведення їх на друк, досить просте:

>>> def fib2(n): # повертає числа Фібоначчі до n
...     """Повертає список чисел Фібоначчі до n"""
...     result = []
...     a, b = 0, 1
...     while b &lt; n:
...         result.append(b) # див. нижче
...         a, b = b, a+b
...     return result
... 
>>> f100 = fib2(100) # виклик функції
>>> f100 # вивід результату
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Цей приклад також демонструє кілька нових властивостей мови Пайтон:

  • Оператор return повертає із функції певне значення. Якщо return вжито без аргументів, то результатом повернення є None. Якщо процедура закінчується сама по собі, то результатом повернення є None.
  • Інструкція result.append(b) викликає метод об'єкта списку result. Метод — це функція, що "належить" певному об'єкту. Вона викликається у формі obj.назва_методу, де obj — певний об'єкт (може також бути виразом) і назва_методу — назва методу, визначеного типом об'єкта. Різні типи визначають різні методи. Методи різних типів можуть мати однакову назву, не спричиняючи при цьому двозначності. (Ви можете також визначити і ваші власні методи за допомогою класів, про що йтиметься далі). Наведений у цьому прикладі метод append() визначений для спискових об'єктів; він додає новий елемент в кінець списку. У цьому прикладі це еквівалентно "result = result + [b]", але цей метод є більш ефективним.

Докладніше про визначення функцій[ред.]

Можливо також визначити функцію зі змінною кількістю аргументів. Для цього існує три способи, що можуть сполучуватися.

Стандартні значення аргументів[ред.]

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

def ask_ok(prompt, retries=4, complaint='Так чи ні, будь-ласка!'):
     while True:
         ok = raw_input(prompt)
         if ok in ('т', 'та', 'так'): return True
         if ok in ('н', 'ні', 'нєт'): return False
         retries = retries  1
         if retries &lt; 0: raise IOError, 'затятий користувач'
         print complaint

Ця функція може викликатися як ask_ok('Справді закрити програму?') чи як ask_ok('Переписати файли?', 2).

Цей приклад також ілюструє ключове слово in, що дозволяє перевірити чи дана послідовність містить певний елемент.

Стандартні значення обчислюються в момент задання функції відповідно до визначаючого контенсту, тому:

i = 5

def f(arg=i):
    print arg

i = 6
f()

виведе 5.

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

def f(a, L=[]):
    L.append(a)
    return L

print f(1)
print f(2)
print f(3)

Це виведе:

[1]
[1, 2]
[1, 2, 3]

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

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L


Ключові аргументи[ред.]

Функції можуть також викликатися за допомогою ключових аргументів у вигляді "ключ = значення". Наприклад, ця функція:

def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
    print "-- This parrot wouldn't", action,
    print "if you put", voltage, "Volts through it."
    print "-- Lovely plumage, the", type
    print "-- It's", state, "!"

може викликатися у будь-який вказаний нижче спосіб:

parrot(action = 'VOOOOOM', voltage = 1000000)
parrot('a thousand', state = 'pushing up the daisies')
parrot('a million', 'bereft of life', 'jump')

але такі виклики неправильні:

parrot() # пропущено обов'язковий аргумент
parrot(voltage=5.0, 'dead') # неключовий аргумент передається після ключового
parrot(110, voltage=220) # два значення для одного аргумента
parrot(actor='John Cleese') # невідомий ключ

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

>>> def function(a):
...     pass
... 
>>> function(0, a=0)
Traceback (most recent call last):
 File "&lt;stdin&gt;", line 1, in ?
TypeError: function() got multiple values for keyword argument 'a'

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

def cheeseshop(kind, *arguments, **keywords):
    print "-- Do you have any", kind, '?'
    print "-- I'm sorry, we're all out of", kind
    for arg in arguments: print arg
    print '-'*40
    keys = keywords.keys()
    keys.sort()
    for kw in keys: print kw, ':', keywords[kw]

можна викликати отак:

cheeseshop('Limburger', "It's very runny, sir.",
        "It's really very, VERY runny, sir.",
        client='John Cleese',
        shopkeeper='Michael Palin',
        sketch='Cheese Shop Sketch')

Що, звичайно, виведе:

-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch

Зауважте, що метод ключових аргументів sort() викликано перед виведенням змісту словника keywords, бо інакше порядок виведення аргументів був би невизначеним.

Списки аргументів довільної довжини[ред.]

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

def fprintf(file, format, *args):
    file.write(format % args)

Розпакування списків аргументів[ред.]

Зворотня ситуація трапляється, коли аргументи задані списком чи кортежем, але їх потрібно розпакувати для виклику функції, що потребує окремих позиційних аргументів. Наприклад, вбудована функція range() потребує двох окремих аргументів, що вказують на межі послідовності. Якщо вони не задані окремо, виклик функції слід писати з оператором *, що дозволяє розпакувати аргументи, задані списком чи кортежем:

>>> range(3, 6) # звичайний виклик з окремими аргументами
[3, 4, 5]
>>> args = [3, 6]
>>> range(*args) # виклик із аргументами, розпакованими зі списку
[3, 4, 5]

Лямбда-функції[ред.]

За популярною вимогою до Пайтона було додано кілька нових властивостей, типових для функціональних мов програмування та мови Lisp. Ключове слово lambda дозволяє створювати невеличкі анонімні функції. Ось, наприклад, функція, що повертає суму двох своїх аргументів: "lambda a, b: a+b". Лямбда-функції можуть стати в нагоді, коли потрібні об'єкти функцій. Синтаксично вони обмежені одним єдиним виразом. Семантично вони просто синтаксичний цукор нормального визначення функції. Подібно до вкладених функцій лямбда-функції можуть посилатися на змінні із зовнішнього контексту:

>>> def make_incrementor(n):
...    return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43

Рядки документації[ред.]

Сформована певна домовленість щодо форматування документації.

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

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

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

Ось приклад багаторядкової документації:

>>> def my_function():
...     """Не робить нічого, лише містить документацію.
... 
...     Справді, ця функція не робить нічого.
...     """
...     pass
... 
>>> print my_function.__doc__
Не робить нічого, лише містить документацію.
 
     Справді, ця функція не робить нічого.