def ahoj(name="Daniel"):
return f"Ahoj, {name}!"
pozdrav = ahoj
print(pozdrav()) # "Ahoj, Daniel!"
print(pozdrav("Svet")) # "Ahoj, Svet!"
V Pythone sú funkcie plnohodnotné objekty, čo znamená:
Funkcia môže obsahovať typové anotácie a dokumentačné reťazce
def add(a: int, b: int) -> int:
"""Vráti súčet dvoch čísel."""
return a + b
Testy v dokumentácii funkcie:
Dokumentačný reťazec môže obsahovať aj príklady použitia, ktoré sú zároveň testovateľné pomocou modulu doctest.
def add(a: int, b: int) -> int:
"""Vráti súčet dvoch čísel.
>>> add(2, 3)
5
>>> add(-1, 1)
0
"""
return a + b
Funkciu je možné uložiť do premennej, alebo odobzdať ako hodnotu
Lambda = anonymná (krátka) funkcia
square = lambda x: x * x
print(square(4))
Pomocou sorted a lambda je možné triediť zoznamy slovníkov podľa kľúča.
Lambda vyjadruje spôsob ako získať hodnotu, podľa ktorej sa má triediť.
data = [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25},
{"name": "Charlie", "age": 35},
]
sorted_data = sorted(data, key=lambda x: x["age"])
# sorted je generátor
yielddef generate_numbers(n):
for i in range(n):
yield i * 2
for number in generate_numbers(10):
print(number)
Rekurzia znamená, že funkcia volá samu seba.
Používa sa pri problémoch, ktoré sa dajú rozdeliť na menší problém rovnakého typu:
def sum_list(items):
if not items:
return 0
return items[0] + sum_list(items[1:])
print(sum_list([1, 2, 3, 4]))
Výsledok:
10
Každá rekurzívna funkcia potrebuje:
def factorial(n):
if n == 0: # podmienka ukončenia
return 1
return n * factorial(n - 1) # rekurzívny krok
factorial(4)
= 4 * factorial(3)
= 4 * 3 * factorial(2)
= 4 * 3 * 2 * factorial(1)
= 4 * 3 * 2 * 1 * factorial(0)
= 4 * 3 * 2 * 1 * 1
= 24
Najprv sa volania skladajú na zásobník, potom sa výsledky postupne vracajú späť.
Namiesto opakovania pomocou cyklu môžeme problém zmenšovať pomocou volania tej istej funkcie.
def factorial(n):
if n == 0:
return 1
return n * factorial(n - 1)
Funkcionálne programovanie (FP) je štýl programovania, kde:
+---------+
vstup -> | funkcia | -> výstup
+---------+
Čisté funkcie nemajú vedľajšie efekty
stav 1 je vstup -> funkcia -> výstup je stav 2
def add(a, b):
return a + b
Funkcia s vedľajším efektom
stav
^ |
| v
+---------+
vstup -> | funkcia | -> výstup
+---------+
|
v
vedľajší efekt
Funkcia s vedľajším efektom zmení stav sveta okolo seba (terminál, súbor, sieť, pamäť...)
Dve zavolania takej funkcie môžu mať dva rôzne výsledky
total = 0
def add(a):
global total
print("Ahoj Svet")
total += a
def add(a, b):
return a + b
assert add(2, 3) == 5
Funkcia, ktorá:
def apply_function(f, x):
return f(x)
print(apply_function(lambda x: x * 2, 5))
+-----------------+ +---------+ +-----------------+
| vstupný zoznam | → | funkcia | → | výstupný zoznam |
+-----------------+ +---------+ +-----------------+
Zoznam vstupov vytvorí zoznam výstupov podľa zadanej funkcie.
numbers = [1, 2, 3]
result = list(map(lambda x: x * 2, numbers))
Podľa zadanej funkcie vyberie zoznam vstupov, ktoré spĺňajú podmienku.
numbers = [1, 2, 3, 4]
result = list(filter(lambda x: x % 2 == 0, numbers))
+---------+ +---------+ +--------+
| zoznam | → | funkcia | → |hodnota |
+---------+ +---------+ +--------+
Funkcia má dva operandy. Prvý operand je akumulátor, druhý je aktuálny prvok zoznamu. Výsledok sa stáva novým akumulátorom.
akumlátor = f(akumulátor, prvok)
Príklad: súčet zoznamu pomocu reduce
from functools import reduce
numbers = [1, 2, 3, 4]
result = reduce(lambda x, y: x + y, numbers)
Python má elegantnejšiu alternatívu k map a filter:
numbers = [1, 2, 3, 4]
result = [x * 2 for x in numbers if x % 2 == 0]
Uzáver zostaví funkciu na požiadanie podľa zadaných parametrov.
def multiplier(n):
def multiply(x):
return x * n
return multiply
double = multiplier(2)
print(double(5))
Dekorátor je funkcia, ktorá:
import time
def meraj_cas(f):
def obalka(*args, **kwargs):
start = time.perf_counter()
result = f(*args, **kwargs)
end = time.perf_counter()
print(f"{f.__name__}: {end - start:.4f}s")
return result
return obalka
@mer_cas
def pomaly_vypocet():
total = sum(i ** 2 for i in range(10_000_000))
return total
pomalý_výpočet()
# pomalý_výpočet: 2.1342s
@lru_cacheSystém si zapamätá výsledky funkcie pre rovnaké vstupy, takže sa nemusí opakovane počítať.
Funkcia by mala byť čistá (bez vedľajších efektov), aby sa dala bezpečne cache-ovať.
Urýchľuje rekurziu
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print(fib(50)) # 12586269025 — okamžite (niekoľko ms)
# bez @lru_cache by trvalo minúty
Parametre cez dvojitý uzáver:
def opakovane(n):
def decorator(f):
def obalka(*args, **kwargs):
for _ in range(n):
result = f(*args, **kwargs)
return result
return obalka
return decorator
@opakovane(3)
def ahoj():
print("Ahoj")
ahoj()
# Ahoj
# Ahoj
# Ahoj
Problém: Zoznam objednávok odfiltrovať tie s nulovou sumou, vypočítať DPH z každej a získať celkovú sumu bez DPH.
orders = [
{"id": 1, "amount": 100, "vat_rate": 0.20},
{"id": 2, "amount": 0, "vat_rate": 0.20}, # vyraď
{"id": 3, "amount": 250, "vat_rate": 0.10},
{"id": 4, "amount": 80, "vat_rate": 0.20},
]
total = 0
for order in orders:
if order["amount"] > 0:
vat = order["amount"] * order["vat_rate"]
total += order["amount"]
active = [o for o in orders if o["amount"] > 0]
with_vat = [{**o, "vat": o["amount"] * o["vat_rate"]} for o in active]
total = sum(o["amount"] for o in with_vat)
from functools import reduce
# 1. Odfiltrujeme nulové objednávky
active = list(filter(lambda o: o["amount"] > 0, orders))
# 2. Pripočítame DPH
with_vat = list(map(
lambda o: {**o, "vat": o["amount"] * o["vat_rate"]},
active
))
# 3. Spočítame celkovú sumu bez DPH
total = reduce(lambda acc, o: acc + o["amount"], with_vat, 0)
filter, map a reduce sa dajú paralelizovať, pretože každé volanie funkcie je nezávislé od ostatných.
from multiprocessing import Pool
def process_order(order):
if order["amount"] > 0:
vat = order["amount"] * order["vat_rate"]
return {**order, "vat": vat}
return None
# Vytvoríme pool procesov
with Pool() as pool:
# spracovanie objednávok paralelne
results = pool.map(process_order, orders)
# odfiltrujeme None hodnoty
results = [r for r in results if r is not None]
# spočítame celkovú sumu bez DPH
total = sum(r["amount"] for r in results)
Hadoop a Spark sú distribuované výpočtové systémy, ktoré využívajú map a reduce na spracovanie veľkých dát.
Príklad: počítanie počtu IP adries z logov z webového servera
MAP: rozdelíme logy na menšie časti a počítame IP adresy pre pre každú časť osobitne paralelne na viacerých uzloch.
REDUCE: zjednotíme čiastkové výsledky z jednotlivých uzlov do jedného výsledku.
Teaser: Objektové programovanie je opak funkcionálneho - metródy tried menia stav objektu..