Към съдържанието
wannacode

Стойности по подразбиране

Проблем

Две покупки, три покупки

Касата трябва да сумира цените на покупките. Понякога са две, понякога три. Функцията е една и съща по смисъл: „сумирай“. В Java и C# това се прави с два метода с едно име и различен брой параметри. Пробваш същото:

def total(a, b):
return a + b
def total(a, b, c):
return a + b + c
print(total(2.5, 4.0, 1.5))
print(total(2.5, 4.0))
Изход
8.0

С три аргумента работи. С два програмата спира:

Грешка
TypeError: total() missing 1 required positional argument: 'c'

Python казва, че на total липсва c. Значи вика функцията с трите параметъра. Къде отиде първата?

Идеята

Едно име, една функция

В Python def е команда като всички останали. Изпълнява се, когато програмата стигне до нея, и записва функцията под име. Второ def със същото име записва нова функция под същото име, а старата се губи. Кое def е последно преди извикването, това решава коя функция работи.

Затова Python няма претоварване (overloading): две функции с едно име, които се различават по параметрите, както в Java и C#. Вместо това една функция може да приема различен брой аргументи. Как, ще видиш в следващите карти.

def print_header():
print("Квартален магазин")
print_header()
def print_header():
print("Магазин „Център“")
print_header()
Стъпка по стъпка, с думи
  1. def записва функцията под името print_header. Името сочи нея.
  2. Извикване: името сочи първата функция и тя печата „Квартален магазин“.
  3. Второто def прави нова функция и насочва същото име към нея. Първата я няма.
  4. Второто извикване: „Магазин „Център““.
  5. Името е едно. Последното def, до което е стигнала програмата, решава коя функция се вика.

Пробвай

Коя функция?

Какво ще отпечата програмата?

def describe(x):
print(f"цяло: {x}")
def describe(x):
print(f"дробно: {x}")
describe(5)
describe(5.0)
Отговор

Идеята

Стойност по подразбиране

Една функция стига и за две, и за три покупки. Дай на третия параметър стойност по подразбиране: тя се ползва, когато аргументът не е подаден.

def total(a, b, c=0):
return a + b + c
print(total(2.5, 4.0))
print(total(2.5, 4.0, 1.5))
Изход
6.5
8.0

c=0 значи: ако при извикването има само два аргумента, c е 0. Ако има три, c е третият.

В Python около = в стойността по подразбиране не се слагат интервали: c=0. Така пише ръководството PEP 8.

Параметрите със стойност по подразбиране се пишат след тези без такава. Аргументите се свързват с параметрите по място, затова незадължителните стоят накрая.

По-точно, за любопитните

Стойността по подразбиране се смята веднъж, когато Python стигне до def, а не при всяко извикване. За числа и текст това няма значение. Но списък по подразбиране е един и същ за всички извиквания:

def add_item(item, cart=[]):
cart.append(item)
return cart
print(add_item("хляб"))
print(add_item("мляко"))
Изход
['хляб']
['хляб', 'мляко']

Второто извикване продължава списъка на първото. Затова за списък по подразбиране се пише None и списъкът се прави във функцията:

def add_item(item, cart=None):
if cart is None:
cart = []
cart.append(item)
return cart
print(add_item("хляб"))
print(add_item("мляко"))
Изход
['хляб']
['мляко']

Пробвай

Намери грешката

Програмата не тръгва. Python спира със SyntaxError, преди да изпълни и един ред. На кой ред е грешката?

Избери реда с грешката.

Идеята

Аргументи по име

Хотелът записва резервации. Една нощувка без закуска е най-честата, затова двете са по подразбиране:

def book(name, nights=1, breakfast=False):
print(f"{name}, нощувки: {nights}")
if breakfast:
print(" със закуска")
book("Мария")
book("Иван", 3)
book("Петя", breakfast=True)
book(nights=2, name="Георги")
Изход
Мария, нощувки: 1
Иван, нощувки: 3
Петя, нощувки: 1
със закуска
Георги, нощувки: 2

Петя иска закуска, но една нощувка. Не е нужно да пишеш нощувките: breakfast=True подава аргумента по име, направо на параметъра breakfast. Това се казва аргумент по име (keyword argument). С имена редът няма значение: Георги подава и двата аргумента по име, в обратен ред.

Името трябва да е точно. Сгрешено име не минава тихо, а спира програмата:

Грешка
book("Мария", night=2)
TypeError: book() got an unexpected keyword argument 'night'
По-точно, за любопитните
  • След аргумент по име не може да има аргумент без име: book(name="Мария", 3) не тръгва („positional argument follows keyword argument“).
  • Аргументите по име правят извикването по-ясно и когато не пропускаш нищо: book("Иван", nights=3) се чете по-лесно от book("Иван", 3).

Идеята

Колкото искаш аргументи

А ако покупките са пет? Двайсет? Не пишеш параметър за всяка. Казваш, че функцията приема произволен брой аргументи:

def total(*prices):
result = 0
for p in prices:
result += p
return result
print(total(1.5, 2.0, 3.5, 4.0))
print(total(9.99))
print(total())
Изход
11.0
9.99
0

Звездичката пред името (*prices) значи: „подай колкото искаш стойности“. Вътре във функцията те пристигат като кортеж (tuple): поредица като списък, която не може да се променя. Без аргументи той е празен.

def show(*prices):
print(prices)
show(1.5, 2.0)
Изход
(1.5, 2.0)

Затова и print печата колкото стойности му дадеш: това е една функция, която приема произволен брой аргументи.

print("Хляб", "Мляко", "Кафе")
print(max(3, 8, 2))
Изход
Хляб Мляко Кафе
8
По-точно, за любопитните
  • Такъв параметър може да е само един. Параметрите след него се подават само по име: def receipt(*prices, currency="€") се вика с receipt(2.5, 4.0, currency="EUR").
  • Има и **options: той събира аргументите по име в речник. Ще го срещнеш в чужд код.

Пробвай

Какво липсва?

def total(prices):
    result = 0
    for p in prices:
        result += p
    return result

Упражнение

Една функция вместо няколко

1. Най-голямото. Напиши една функция largest(*numbers), която връща най-голямото от колкото и да е числа.

Извикване
largest(3, 8)
largest(4, 9, 2)
largest(2.5, 1.5)
Изход
8
9
2.5

2. Среден успех. Функция average(*grades) връща средното на произволен брой оценки, а без оценки връща 0.

Извикване
average(5, 6, 4)
average(6, 5)
average()
Изход
5.0
5.5
0

3. Ред от бележката. print_line(item, qty) печата „Кафе x 2“. Направи бройката незадължителна: без нея е 1.

Извикване
print_line("Кафе", 2)
print_line("Кроасан")
Изход
Кафе x 2
Кроасан x 1

4. Безплатна доставка. shipping_cost(total, free_from=50) връща 0, ако поръчката е поне колкото прага, иначе 4,99 €. Прагът е 50 €, ако не е подаден друг.

Извикване
shipping_cost(62.0)
shipping_cost(30.0)
shipping_cost(30.0, free_from=25)
Изход
0
4.99
0

Тест

Провери се

1. Във файла има def total(a, b): и под него def total(a, b, c):. Какво става при total(2, 3)?
2. def total(a, b, c=0): Какво връща total(2, 3)?
3. Как се извиква def book(name, nights=1, breakfast=False):, за да има закуска, а нощувките да останат 1?
4. Какво е prices вътре в def total(*prices):?
5. Кое от тези Python НЕ пуска?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·