Funkce a jejich argumenty. Vrácené hodnoty z funkce. návratový výpis

Existuje velké množství publikací věnovaných implementaci konceptů funkcionálního programování v Pythonu, ale většinu těchto materiálů napsal jeden autor – David Mertz. Navíc mnoho z těchto článků je již zastaralých a roztroušených po různých online zdrojích. V tomto článku se pokusíme znovu navštívit toto téma, abychom obnovili a uspořádali dostupné informace, zejména s ohledem na velké rozdíly, které existují mezi verzemi Pythonu na řádcích 2 a 3.

Funkce v Pythonu

Funkce v Pythonu jsou definovány dvěma způsoby: prostřednictvím definice def nebo pomocí anonymního popisu lambda. Obě tyto definiční metody jsou v různé míře dostupné v některých jiných programovacích jazycích. Rysem Pythonu je, že funkce je pojmenovaný objekt stejně jako jakýkoli jiný objekt nějakého datového typu, řekněme celočíselná proměnná. Výpis 1 ukazuje nejjednodušší příklad (soubor func.py z archivu python_functional.tgz

Výpis 1. Definice funkcí
#!/usr/bin/python # -*- kódování: utf-8 -*- import sys def show(fun, arg): print("() : ()".format(type(fun), fun))) print("arg=() => fun(arg)=()".format(arg, fun(arg))) if len(sys.argv) > 1: n = float(sys.argv[ 1 ]) else : n = float(input("číslo?: ")) def pow3(n): # 1. definice funkce return n * n * n show(pow3, n) pow3 = lambda n: n * n * n # 2 -th definice funkce se stejným názvem show(pow3, n) show((lambda n: n * n * n), n) # 3., použití definice anonymní funkce

Když zavoláme všechny tři funkční objekty, dostaneme stejný výsledek:

$ python func.py 1.3 : arg=1,3 => zábava(arg)=2,197 : at 0xb7662bc4> arg=1.3 => fun(arg)=2.197 : at 0xb7662844> arg=1.3 => fun(arg)=2.197

To je ještě výraznější v Pythonu verze 3, ve které je vše třída (včetně celočíselné proměnné) a funkce jsou programové objekty, které patří do třídy. funkce:

$python3 func.py 1.3 : arg=1,3 => zábava(arg)=2,19700000000000005 : at 0xb745432c> arg=1.3 => fun(arg)=2.1970000000000005 : at 0xb74542ec> arg=1.3 => fun(arg)=2.1970000000000005

Poznámka. Existují ještě 2 typy objektů, které umožňují volání funkce – metoda funkční třídy a funktor, o kterých si povíme později.

Pokud jsou funkční objekty Pythonu objekty stejně jako jiné datové objekty, můžete s nimi dělat vše, co můžete dělat s jakýmikoli daty:

  • dynamicky přeměna probíhá;
  • vkládat do složitějších datových struktur (kolekcí);
  • předat jako parametry a návratové hodnoty atd.

Na tomto (manipulace s funkčními objekty jako datovými objekty) je založeno funkcionální programování. Python samozřejmě není skutečný funkční programovací jazyk, například existují speciální jazyky pro plně funkční programování: Lisp, Planner a novější: Scala, Haskell. Ocaml, ... Ale v Pythonu můžete „vložit“ techniky funkcionálního programování do obecného toku imperativního (příkazového) kódu, například použít metody vypůjčené z plnohodnotných funkcionálních jazyků. Tito. „skládat“ jednotlivé fragmenty imperativního kódu (někdy poměrně velké) do funkčních výrazů.

Někdy se lidé ptají: "Jaké jsou výhody funkčního stylu psaní jednotlivých fragmentů pro programátora?" Hlavní výhodou funkčního programování je, že po jednorázovém odladění takového fragmentu nezpůsobí následné opakované použití chyby v důsledku vedlejších efektů spojených s přiřazením a konflikty jmen.

Poměrně často se při programování v Pythonu používají typické konstrukce z oblasti funkcionálního programování, například:

tisknout ([ (x,y) pro x v (1, 2, 3, 4, 5) \ pro y v (20, 15, 10) \ pokud x * y > 25 a x + y< 25 ])

V důsledku běhu získáme:

$ python funcp.py [(2,20), (2,15), (3,20), (3,15), (3,10), (4,20), (4,15), (4 ,10), (5,15), (5,10)]

Funguje jako objekty

Při vytváření funkčního objektu s operátorem lambda, jak je uvedeno ve výpisu 1, můžete svázat vytvořený funkční objekt s názvem pow3 přesně stejným způsobem, jako by se dalo k tomuto jménu připojit číslo 123 nebo řetězec "Ahoj!". Tento příklad potvrzuje stav funkcí jako prvotřídních objektů v Pythonu. Funkce v Pythonu je jen další hodnota, se kterou můžete něco dělat.

Nejběžnější akcí prováděnou s prvotřídními funkčními objekty je jejich předání vestavěným funkcím vyššího řádu: mapa(), snížit() A filtr(). Každá z těchto funkcí má jako svůj první argument objekt funkce.

  • mapa() aplikuje předávanou funkci na každý prvek v předaném seznamu (seznamech) a vrátí seznam výsledků (stejný rozměr jako vstup);
  • snížit() aplikuje předávanou funkci na každou hodnotu v seznamu a na interní akumulátor výsledků, např. snížit(lambda n,m: n * m, rozsah(1, 10)) prostředek 10! (faktoriální);
  • filtr() aplikuje předávanou funkci na každý prvek seznamu a vrátí seznam těch prvků původního seznamu, pro které předaná funkce vrátila hodnotu true.

Kombinací těchto tří funkcí můžete implementovat překvapivě širokou škálu operací řídicího toku, aniž byste se uchýlili k imperativním příkazům, ale s použitím pouze výrazů ve funkčním stylu, jak je uvedeno ve výpisu 2 (soubor funcH.py z archivu python_functional.tgz

Výpis 2. Funkce vyššího řádu v Pythonu
#!/usr/bin/python # -*- kódování: utf-8 -*- import sys def input_arg(): global arg arg = (lambda: (len(sys.argv) > 1 a int(sys.argv[ 1 ])) nebo \ int(input("číslo?: ")))() return arg print("argument = ()".format(input_arg())) print(list(map(lambda x: x + 1) , range(arg))) print(list(filter(lambda x: x > 4, range(arg)))) import functools print("()! = ()".format(arg, functools.reduce(lambda x , y: x * y, rozsah (1, arg))))

Poznámka. Tento kód je o něco složitější než předchozí příklad kvůli následujícím problémům s kompatibilitou mezi Pythonem verze 2 a 3:

  • Funkce snížit(), deklarovaný jako vestavěný v Pythonu 2, byl přesunut do modulu v Pythonu 3 functools a zavolání přímo jménem vyvolá výjimku NameError, takže pro správnou funkci musí být volání naformátováno jako v příkladu nebo musí obsahovat řádek: z importu functools *
  • Funkce mapa() A filtr() v Pythonu 3 nevracejí seznam (jak již bylo ukázáno při diskuzi o rozdílech verzí), ale objekty iterátoru formuláře:

Chcete-li získat celý seznam hodnot pro ně, zavolá se funkce seznam().

Proto bude tento kód fungovat v obou verzích Pythonu:

$python3 funcH.py 7 argument = 7 7! = 720

Pokud není vyžadována přenositelnost kódu mezi různými verzemi, pak lze takové fragmenty vyloučit, což umožní kód poněkud zjednodušit.

Rekurze

Ve funkcionálním programování je rekurze základním mechanismem, podobně jako smyčky v iterativním programování.

V některých diskuzích o Pythonu jsem se opakovaně setkal s tvrzeními, že v Pythonu je hloubka rekurze omezena „hardwarem“, a proto některé akce z principu nelze implementovat. Python má výchozí limit hloubky rekurze 1000, ale toto je číselné nastavení, které lze vždy přepsat, jak je znázorněno ve výpisu 3 (úplný příklad kódu naleznete v souboru fakt2.py z archivu python_functional.tgz

Výpis 3. Výpočet faktoriálu s libovolnou hloubkou rekurze
#!/usr/bin/python # -*- kódování: utf-8 -*- import sys arg = lambda: (len(sys.argv) > 1 a int(sys.argv[ 1 ])) nebo \ int( input("číslo?: ")) faktoriál = lambda x: ((x == 1) a 1) nebo x * faktoriál(x - 1) n = arg() m = sys.getrecursionlimit() pokud n >= m - 1: sys.setrecursionlimit(n + 2) print("hloubka rekurze přesahuje hodnotu nastavenou v systému (), reset na ()".\ format(m, sys.getrecursionlimit())) print("n=( ) => n!=()".format(n, faktoriál(n))) if sys.getrecursionlimit() > m: print("hloubka rekurze obnovena na ()".format(m)) sys.setrecursionlimit(m )

Zde je návod, jak vypadá provedení tohoto příkladu v Pythonu 3 a Pythonu2 (i když ve skutečnosti se výsledné číslo pravděpodobně nevejde na jednu obrazovku terminálu konzoly):

$ python3 fact2.py 1001 hloubka rekurze překračuje systémovou sadu 1000, resetováno na 1003 n=1001 => n!=4027........................ . ........................0000000000000 hloubka rekurze obnovena na 1000

Několik jednoduchých příkladů

Proveďme několik jednoduchých transformací obvyklého imperativního kódu (příkaz, operátor), abychom převedli jeho jednotlivé fragmenty na funkční. Nejprve nahraďme větvené operátory logickými podmínkami, které nám díky „odloženým“ (líným, líným) výpočtům umožňují řídit spouštění či nespouštění jednotlivých větví kódu. Takže imperativní konstrukce:

-li<условие>: <выражение 1>jiný:<выражение 2>

Zcela ekvivalentní následujícímu funkčnímu fragmentu (kvůli „odloženým“ schopnostem logických operátorů a A nebo):

# funkce bez parametrů: lambda: (<условие>a<выражение 1>) nebo (<выражение 2>)

Použijme jako příklad opět faktoriální výpočet. Výpis 4 ukazuje funkční kód pro výpočet faktoriálu (soubor fakt1.py v archivu python_functional.tgz v sekci "Stáhnout materiály"):

Výpis 4. Operátorová (imperativní) definice faktoriálu
#!/usr/bin/python # -*- kódování: utf-8 -*- import sys def factorial(n): if n == 1: return 1 else: return n * factorial(n - 1) if len( sys.argv) > 1: n = int(sys.argv[ 1 ]) else: n = int(vstup("číslo?: ")) print("n=() => n!=()".formát (n, faktoriál(n)))

Argument, který se má vypočítat, je převzat z hodnoty parametru příkazového řádku (pokud existuje) nebo zadán z terminálu. První výše uvedená změna je již použita ve výpisu 2, kde byly výrazy funkcí nahrazeny:

  • Definice faktoriálové funkce: faktoriál = lambda x: ((x == 1) a 1) nebo x * faktoriál(x - 1)
  • požadavek na zadání hodnoty argumentu z konzoly terminálu: arg = lambda: (len(sys.argv) > 1 a int(sys.argv[ 1 ])) nebo \ int(input("číslo?: ")) n = arg()

V souboru fakt3.py objeví se další definice funkce, vytvořená pomocí funkce vyššího řádu snížit():

faktoriál = faktoriál = lambda z: snížit(lambda x, y: x * y, rozsah(1, z + 1))

Zde také zjednodušíme výraz pro n, redukuje to na jediné volání anonymní (nejmenované) funkce:

n = (lambda: (délka(sys.argv) > 1 a int(sys.argv[ 1 ])) nebo \ int(vstup("číslo?: ")))()

Nakonec si můžete všimnout, že přiřazení hodnoty proměnné n nutné pouze pro jeho použití při hovoru vytisknout() pro výstup této hodnoty. Pokud toto omezení opustíme, celá aplikace se zvrhne v jeden funkční operátor (viz soubor fact4.py v archivu python_functional.tgz v sekci "Stáhnout materiály"):

from sys import * from functools import reduction print("computed factorial = ()".format(\ (lambda z: reduction(lambda x, y: x * y, range(1, z + 1))) \ ((lambda : (délka(argv) > 1 a int(argv[ 1 ])) nebo \ int(vstup("číslo?: ")))())))

Toto jediné volání uvnitř funkce vytisknout() a představuje celou aplikaci v její funkční podobě:

$python3 fact4.py číslo?: 5 vypočítaný faktoriál = 120

Čte se tento kód (soubor fact4.py) lépe než imperativní zápis (soubor fact1.py)? Spíš ne než ano. Jaká je tedy jeho důstojnost? Faktem je, že kdy žádný změny okolního kódu, normální provoz tento fragment bude zachován, protože nehrozí žádné vedlejší účinky v důsledku změn hodnot použitých proměnných.

Funkce vyššího řádu

Ve stylu funkčního programování je standardní praxe dynamická generace funkční objekt během provádění kódu s jeho následným voláním ve stejném kódu. Existuje řada oblastí, kde by taková technika mohla být užitečná.

Uzavření

Jedním ze zajímavých konceptů funkcionálního programování je uzávěry(uzavření). Tento nápad se ukázal být pro mnoho vývojářů natolik lákavý, že byl dokonce implementován v některých nefunkčních programovacích jazycích (Perl). David Mertz uvádí následující definici uzávěru: „Uzávěr je procedura spolu se množinou dat, která je k ní vázaná“ (na rozdíl od objektů v objektovém programování, jako: „data spolu se sadou procedur s ní vázanými“). .

Význam uzavření spočívá v tom, že definice funkce „zmrazí“ kontext, který ji obklopuje moment odhodlání. To lze provést různými způsoby, například parametrizací vytváření funkce, jak je uvedeno ve výpisu 5 (soubor clos1.py v archivu python_functional.tgz v sekci "Stáhnout materiály"):

Výpis 5. Vytvoření uzávěrky
# -*- kódování: utf-8 -*- def multiplier(n): # multiplikátor vrací funkci, která se násobí n def mul(k): return n * k return mul mul3 = multiplikátor(3) # mul3 je funkce který se vynásobí 3 print(mul3(3), mul3(5))

Takto funguje taková dynamicky definovaná funkce:

$ python clos1.py (9, 15) $ python3 clos1.py 9 15

Dalším způsobem, jak vytvořit uzávěr, je použít výchozí hodnotu parametru v bodě definice funkce, jak je uvedeno ve výpisu 6 (soubor clos3.py z archivu python_functional.tgz v sekci "Stáhnout materiály"):

Výpis 6. Další způsob vytvoření uzávěru
n = 3 def mult(k, mul = n): return mul * k n = 7 print(mult(3)) n = 13 print(mult(5)) n = 10 mult = lambda k, mul=n: mul * k tisk (více(3))

Žádné následné přiřazení k výchozímu parametru nezmění dříve definovanou funkci, ale samotnou funkci lze přepsat:

$ python clos3.py 9 15 30

Aplikace dílčích funkcí

Částečná funkce aplikace předpokládá na základě funkce N proměnných definice nové funkce s menším počtem proměnných M < N, zatímco zbytek N-M proměnné dostávají pevné „zamrzlé“ hodnoty (používá se modul functools). Podobný příklad bude diskutován níže.

Funktor

Funktor není funkce, ale objekt třídy, ve kterém je volána metoda __volání__(). V tomto případě lze volání aplikovat na instanci takového objektu, stejně jako se to děje u funkcí. Ve výpisu 7 (soubor part.py z archivu python_functional.tgz(viz sekce Ke stažení) ukazuje, jak použít uzávěr, částečnou definici funkce a funktor k dosažení stejného výsledku.

Výpis 7. Porovnání uzávěru, parciální definice a funktoru
# -*- kódování: utf-8 -*- def multiplier(n): # closure def mul(k): return n * k return mul mul3 = multiplier(3) z functools import částečného def mulPart(a, b ): # částečná aplikace funkce return a * b par3 = částečný(mulPart, 3) class mulFunctor: # ekvivalentní funktor def __init__(self, val1): self.val1 = val1 def __call__(self, val2): return self.val1 * val2 fun3 = mulFunctor(3) print("() . () . ()".format(mul3(5), par3(5), fun3(5))))

Volání všech tří konstrukcí pro argument rovný 5 povede ke stejnému výsledku, i když budou používat zcela odlišné mechanismy:

$ python part.py 15 . 15. 15

Karring

Curring (nebo currying) je transformace funkce z mnoha proměnných na funkci, která přebírá své argumenty jeden po druhém.

Poznámka. Tuto transformaci představili M. Sheinfinkel a G. Frege a byla pojmenována po matematikovi Haskell Currym, po kterém je pojmenován i programovací jazyk Haskell.

Curring není jedinečnou vlastností funkcionálního programování; currying transformaci lze napsat například v Perlu nebo C++. Operátor kari je dokonce zabudován do některých programovacích jazyků (ML, Haskell), což umožňuje vícemístným funkcím vést k curried reprezentaci. Ale všechny jazyky, které podporují uzávěry, vám umožňují psát curried funkce a Python není v tomto ohledu výjimkou.

Výpis 8 ukazuje jednoduchý příklad s použitím kari (soubor kari1.py v archivu python_functional.tgz v sekci "Stáhnout materiály"):

Výpis 8. Curring
# -*- kódování: utf-8 -*- def spam(x, y): print("param1=(), param2=()".format(x, y)) spam1 = lambda x: lambda y: spam (x, y) def spam2(x) : def new_spam(y) : return spam(x, y) return new_spam spam1(2)(3) # spam2(2)(3)

Provádění těchto hovorů vypadá takto:

$ python curry1.py param1=2, param2=3 param1=2, param2=3

Závěr

Tento článek představil některé funkce jazyka Python, které vám umožňují používat jej k psaní programů pomocí funkčního programovacího stylu. Popsali jsme tedy základní techniky funkcionálního programování a ukázali příklady jejich implementace v Pythonu. Stejně jako v předchozích článcích jsou příklady kódu napsány tak, aby je bylo možné úspěšně spustit a spustit v obou verzích Pythonu.

V příštím článku probereme otázky organizace paralelního provádění kódu v prostředí Pythonu.

Funkce je izolovaný blok kódu, který je navržen k provádění konkrétního úkolu a lze jej znovu použít. Díky funkcím je programový kód modulární a kompaktnější.

Python nabízí mnoho vestavěných funkcí. Tyto pravděpodobně znáte:

  • print() vytiskne objekt na terminál.
  • int() převádí řetězce nebo čísla na celá čísla.
  • len() vrací délku objektu.

Název funkce obsahuje závorky a může obsahovat parametry.

Tento tutoriál vás naučí, jak definovat vlastní funkce.

Definice funkce

Nejprve zkuste převést jednoduchý program „Ahoj, světe!“. do funkce.

Vytvořte nový textový soubor hello.py a poté definujte funkci.

Chcete-li definovat funkci, použijte klíčové slovo def, poté zadejte název funkce a závorky, které mohou obsahovat parametry (pokud žádné parametry nejsou, závorky zůstanou prázdné), následované dvojtečkou.

Chcete-li definovat funkci hello(), přidejte do souboru hello.py následující:

Toto je původní definice funkce.

def ahoj():
tisk ("Ahoj, světe!")

Funkce je nyní definována. Pokud ale program v této fázi spustíte, nic se nestane: aby funkce fungovala, musí být nejen definována, ale také volána.

Po definování funkce ji zavolejte:

def ahoj():
tisk ("Ahoj, světe!")
Ahoj()

Nyní spusťte program:

Mělo by se vrátit:

Funkce hello() je poměrně jednoduchý příklad. Funkce mohou být mnohem složitější, mohou obsahovat cykly for, podmíněné výrazy a další komponenty.

Když funkce dosáhne příkazu return, přestane se provádět.

# Soubor return_loop.py
def loop_five():
pro x v rozsahu(0, 25):
tisknout (x)
pokud x == 5:
# Funkce zastavení v x == 5
návrat
print("Tento řádek se nespustí.")
loop_five()

Příkaz return ve smyčce for zastaví provádění funkce, takže řádky za funkcí nebudou provedeny. Pokud by byl místo toho použit příkaz break, program by zastavil provádění cyklu a nikoli funkci a byl by zpracován poslední příkaz print().

funkce main().

V Pythonu lze funkci zavolat na konci programu a spustí se (jak je ukázáno v předchozích příkladech), ale některé programovací jazyky (jako C++ nebo Java) vyžadují hlavní funkci. Funkce main() v Pythonu je volitelná, ale umožňuje vám logicky strukturovat váš Python program a kombinovat jeho nejdůležitější součásti do jediné funkce. Programátorům, kteří pracují v jiných jazycích, také usnadňuje čtení programů v Pythonu.

Vraťte se do souboru hello.py a přidejte funkci main(), přičemž funkci hello() ponechejte.

def ahoj():
tisk ("Ahoj, světe!")
def main():

Přidejte příkaz print() do funkce main(). Poté zavolejte funkci hello() uvnitř funkce main().

def ahoj():
tisk ("Ahoj, světe!")
def main():
print("Toto je hlavní funkce")
Ahoj()

Na konci souboru zavolejte funkci main().

def ahoj():
tisk ("Ahoj, světe!")
def main():
print("Toto je hlavní funkce.")
Ahoj()
hlavní()

Nyní můžete spustit program:

python hello.py
Toto je hlavní funkce.
Ahoj světe!

Nyní zkuste použít několik funkcí.

Připomeňme, že v matematice je faktoriál čísla n definován jako n! = 1 ⋅ 2 ⋅ ... ⋅ n. Například 5! = 1 ⋅ 2 ⋅ 3 ⋅ 4 ⋅ 5 = 120. Je jasné, že faktoriál lze snadno vypočítat pomocí cyklu for. Představme si, že potřebujeme v našem programu (nebo na různých místech v kódu) spočítat faktoriál různých čísel vícekrát. Faktoriální výpočet můžete samozřejmě napsat jednou a poté jej pomocí Copy-Paste vložit kamkoli potřebujete.

# pojďme spočítat 3! res = 1 pro i v rozsahu (1, 4): res *= i print(res) # vypočítat 5! res = 1 pro i v rozsahu (1, 6): res *= i print(res)

Pokud se však jednou spleteme v počátečním kódu, pak se tato chyba objeví v kódu na všech místech, kam jsme zkopírovali výpočet faktoriálu. A obecně kód zabírá více místa, než by mohl. Aby se zabránilo psaní stejné logiky znovu a znovu, programovací jazyky mají funkce.

Funkce jsou části kódu, které jsou izolované od zbytku programu a jsou prováděny pouze tehdy, když jsou volány. Funkce sqrt(), len() a print() jste již viděli. Všechny mají společnou vlastnost: mohou přebírat parametry (nula, jedna nebo více) a mohou vracet hodnotu (i když nemusí). Například funkce sqrt() přebírá jeden parametr a vrací hodnotu (kořen čísla). Funkce print() přebírá proměnný počet parametrů a nevrací nic.

Pojďme si ukázat, jak napsat funkci factorial(), která vezme jeden parametr – číslo a vrátí hodnotu – faktoriál tohoto čísla.

Def faktorial(n): res = 1 pro i v rozsahu(1, n + 1): res *= i return res print(faktoriál(3)) print(faktoriál(5))

Uveďme několik vysvětlení. Nejprve musí být kód funkce umístěn na začátek programu, nebo spíše před místo, kde chceme použít funkci factorial(). První řádek tohoto příkladu je popis naší funkce. faktoriál je identifikátor, tedy název naší funkce. Za identifikátorem v závorce je seznam parametrů, které naše funkce přijímá. Seznam se skládá z identifikátorů parametrů oddělených čárkami. V našem případě se seznam skládá z jedné hodnoty n. Na konec řádku se umístí dvojtečka.

Následuje tělo funkce navržené jako blok, tedy odsazené. Uvnitř funkce se vypočítá faktoriál n a uloží se do proměnné res. Funkce končí příkazem return res, který funkci ukončí a vrátí hodnotu proměnné res.

Příkaz return se může objevit kdekoli ve funkci, jeho provedení funkci ukončí a vrátí zadanou hodnotu na místo, kde byla volána. Pokud funkce nevrátí hodnotu, použije se příkaz return bez návratové hodnoty. Funkce, které nepotřebují vracet hodnotu, nemusí mít příkaz return.

Uveďme další příklad. Napišme funkci max(), která vezme dvě čísla a vrátí z nich maximum (ve skutečnosti je taková funkce již zabudována v Pythonu).

10 20 def max(a, b): if a > b: return a else: return b print(max(3, 5)) print(max(5, 3)) print(max(int(input()), int(vstup())))

Nyní můžeme napsat funkci max3(), která vezme tři čísla a vrátí maximum z nich.

Def max(a, b): if a > b: return a else: return b def max3(a, b, c): return max(max(a, b), c) print(max3(3, 5, 4 ))

Vestavěná funkce max() v Pythonu může převzít proměnný počet argumentů a vrátit maximum z nich. Uveďme si příklad, jak lze takovou funkci zapsat.

Def max(*a): res = a pro val v a: if val > res: res = val return res print(max(3, 5, 4))

Všechny parametry předané této funkci budou shromážděny do jedné n-tice s názvem a, jak je označeno hvězdičkou v řádku deklarace funkce.

2. Lokální a globální proměnné

Uvnitř funkce můžete použít proměnné, které jsou deklarovány mimo funkci.

Def f(): print(a) a = 1 f()

Zde je proměnné a přiřazena hodnota 1 a funkce f() tuto hodnotu vypíše, i když proměnná není inicializována před deklarací f. Když je volána f(), a je již přiřazena hodnota, takže f() ji může zobrazit na obrazovce.

Takové proměnné (deklarované mimo funkci, ale přístupné uvnitř funkce) jsou volány globální.

Ale pokud inicializujete proměnnou uvnitř funkce, nebudete moci tuto proměnnou použít mimo funkci. Například:

Def f(): a = 1 f() tisk(a)

Dostaneme chybu NameError: název "a" není definován . Takové proměnné deklarované uvnitř funkce jsou volány místní. Tyto proměnné se po ukončení funkce stanou nedostupnými.

Výsledek bude zajímavý, pokud se pokusíte změnit hodnotu globální proměnné uvnitř funkce:

Def f(): a = 1 tisk(a) a = 0 f() tisk(a)

Budou vytištěna čísla 1 a 0 I když se hodnota proměnné a uvnitř funkce změnila, mimo funkci zůstává stejná! To se provádí za účelem „ochrany“ globálních proměnných před náhodnými změnami funkce. Pokud je například funkce volána ze smyčky pomocí proměnné i a v této funkci bude pro organizaci smyčky použita také proměnná i, pak se tyto proměnné musí lišit. Pokud nerozumíte poslední větě, podívejte se na následující kód a přemýšlejte o tom, jak by to fungovalo, kdyby proměnná i byla změněna uvnitř funkce.

Def factorial(n): res = 1 pro i v rozsahu (1, n + 1): res *= i návrat res pro i v rozsahu (1, 6): print(i, "! = ", faktoriál(i) , sep="")

Pokud by se globální proměnná i změnila uvnitř funkce, dostali bychom toto:

5! = 1 5! = 2 5! = 6 5! = 24 5! = 120

Pokud je tedy uvnitř funkce upravena hodnota nějaké proměnné, pak se proměnná s tímto názvem stane lokální proměnnou a její modifikace nezmění globální proměnnou se stejným názvem.

Formálněji: interpret Pythonu považuje proměnnou za lokální pro danou funkci, pokud její kód obsahuje alespoň jednu instrukci, která hodnotu proměnné upravuje, pak je tato proměnná považována za lokální a nelze ji před inicializací použít. Instrukce, které upravují hodnotu proměnné, jsou operátory = , += , stejně jako použití proměnné jako parametru cyklu for. Navíc, i když instrukce modifikující proměnnou není nikdy provedena, interpret to nemůže zkontrolovat a proměnná je stále považována za lokální. Příklad:

Def f(): print(a) if False: a = 0 a = 1 f()

Dojde k chybě: UnboundLocalError: místní proměnná "a" odkazovaná před přiřazením. Konkrétně ve funkci f() se identifikátor a stává lokální proměnnou, protože funkce obsahuje příkaz, který modifikuje proměnnou a , i když není nikdy provedena (ale interpret to nemůže sledovat). Proto tisk proměnné a má za následek přístup k neinicializované lokální proměnné.

Aby funkce změnila hodnotu globální proměnné, je nutné tuto proměnnou uvnitř funkce deklarovat jako globální pomocí klíčového slova global:

Def f(): globální a a = 1 tisk(a) a = 0 f() tisk(a)

V tomto příkladu se na obrazovce zobrazí 1 1, protože proměnná a je deklarována jako globální a její změna uvnitř funkce vede k tomu, že proměnná bude dostupná mimo funkci.

Je však lepší neměnit hodnoty globálních proměnných uvnitř funkce. Pokud vaše funkce musí změnit nějakou proměnnou, bylo by lepší, kdyby tuto hodnotu vrátila a vy sami tuto hodnotu proměnné při volání funkce výslovně přiřadíte. Pokud dodržíte tato pravidla, pak jsou funkce nezávislé na kódu a lze je snadno kopírovat z jednoho programu do druhého.

Řekněme například, že váš program potřebuje vypočítat faktoriál vstupního čísla, které pak chcete uložit do proměnné f. Tak to je nestojí za to dělat:

5 def faktoriál(n): globální f res = 1 pro i v rozsahu(2, n + 1): res *= i f = res n = int(vstup()) faktoriál(n) # další akce s proměnnou f

Tento kód je špatně napsaný, protože je obtížné jej znovu použít. Pokud zítra potřebujete použít faktoriál v jiném programu, nemůžete tuto funkci jednoduše zkopírovat odtud a vložit ji do svého nového programu. Budete muset změnit způsob, jakým vrací vypočítanou hodnotu.

Mnohem lepší je tento příklad přepsat takto:

5 # začátek části kódu, kterou lze zkopírovat z programu do programu def factorial(n): res = 1 pro i v rozsahu (2, n + 1): res *= i return res # konec části kódu n = int(input()) f = faktoriál(n) # dále všemožné akce s proměnnou f

Pokud potřebujete, aby funkce nevracela jednu hodnotu, ale dvě nebo více, pak pro tento účel může funkce vrátit seznam dvou nebo více hodnot:

Pak lze výsledek volání funkce použít ve vícenásobném přiřazení:

3. Rekurze

Def short_story(): print("Kněz měl psa, miloval ji.") print("Snědla kus masa, on ji zabil,") print("Zakopal to do země a napsal nápis: ") short_story()

Jak jsme viděli výše, funkce může volat jinou funkci. Ale funkce se může také volat sama! Podívejme se na to na příkladu pomocí funkce výpočtu faktoriálu. Je dobře známo, že 0!=1, 1!=1. Jak vypočítat hodnotu n! pro velké n? Pokud bychom dokázali vypočítat hodnotu (n-1)!, pak můžeme snadno vypočítat n!, protože n!=n⋅(n-1)!. Ale jak vypočítat (n-1)!? Pokud jsme počítali (n-2)!, pak můžeme počítat i (n-1)!=(n-1)⋅(n-2)!. Jak vypočítat (n-2)!? Pokud... Nakonec se dostaneme k hodnotě 0!, která se rovná 1. Pro výpočet faktoriálu tedy můžeme použít hodnotu faktoriálu pro menší číslo. To lze také provést v programu Python:

Def factorial(n): if n == 0: return 1 else: return n * factorial(n - 1) print(factorial(5))

Tato technika (funkce volající sama sebe) se nazývá rekurze a funkce samotná se nazývá rekurzivní.

Rekurzivní funkce jsou mocným mechanismem v programování. Bohužel nejsou vždy účinné. Také použití rekurze často vede k chybám. Nejběžnější z těchto chyb je nekonečná rekurze, kdy řetězec volání funkcí nikdy nekončí a pokračuje, dokud počítači nedojde volná paměť. Příklad nekonečné rekurze je uveden v epigrafu k této části. Dva nejčastější důvody pro nekonečnou rekurzi jsou:

  1. Nesprávný výstup z rekurze. Pokud například zapomeneme zkontrolovat, zda n == 0 v programu pro výpočet faktoriálu, pak faktorial(0) zavolá faktorial(-1), který zavolá faktorial(-2) atd.
  2. Rekurzivní volání s neplatnými parametry. Pokud například funkce factorial(n) zavolá faktorial(n) , výsledkem bude také nekonečný řetězec.

Proto při vývoji rekurzivní funkce musíte nejprve formalizovat podmínky pro ukončení rekurze a přemýšlet o tom, proč se rekurze vůbec ukončí.

Poslední aktualizace: 04/11/2018

Funkce představují blok kódu, který provádí konkrétní úkol a který lze znovu použít v jiných částech programu. Formální definice funkce:

def název_funkce ([parametry]): instrukce

Definice funkce začíná výrazem def, který se skládá z názvu funkce, sady závorek s parametry a dvojtečky. Parametry v závorkách jsou volitelné. A od dalšího řádku je blok instrukcí, které funkce vykonává. Všechny funkční instrukce jsou odsazeny od začátku řádku.

Například definice nejjednodušší funkce:

Def say_hello(): print("Ahoj")

Funkce se nazývá say_hello. Nemá žádné parametry a obsahuje jednu jedinou instrukci, která vypíše do konzole řetězec „Ahoj“.

Chcete-li volat funkci, zadejte název funkce, po kterém následuje přenos hodnot všech jejích parametrů v závorkách. Například:

Def say_hello(): print("Ahoj") say_hello() say_hello() say_hello()

Zde je funkce say_hello volána třikrát za sebou. V důsledku toho obdržíme následující výstup konzoly:

Dobrý den Dobrý den

Nyní pojďme definovat a používat funkci s parametry:

Def say_hello(name): print("Ahoj,",jméno) say_hello("Tom") say_hello("Bob") say_hello("Alice")

Funkce přebírá parametr name a při volání funkce můžeme místo parametru předat hodnotu:

Ahoj, Tome, Ahoj, Bobe, Ahoj, Alice

Výchozí hodnoty

Některé parametry funkce můžeme nastavit jako volitelné tím, že pro ně při definování funkce zadáme výchozí hodnoty. Například:

Def say_hello(name="Tom"): print("Ahoj,", jméno) say_hello() say_hello("Bob")

Zde je parametr name volitelný. A pokud mu při volání funkce nepředáme hodnotu, tak se použije výchozí hodnota, tedy řetězec „Tom“.

Pojmenované parametry

Když jsou hodnoty předány, funkce je spojí s parametry v pořadí, v jakém jsou předány. Řekněme například, že existuje následující funkce:

Def display_info(jméno, věk): print("Jméno:", jméno, "\t", "Věk:", věk) display_info("Tom", 22)

Při volání funkce se první hodnota "Tom" předá prvnímu parametru - parametru name, druhá hodnota - číslo 22 se předá druhému parametru - věku. A tak dále v pořadí. Použití pojmenovaných parametrů umožňuje přepsat předávací pořadí:

Def display_info(jméno, věk): print("Jméno:", jméno, "\t", "Věk:", věk) display_info(věk=22, jméno="Tom")

Pojmenované parametry vyžadují při volání funkce specifikovat název parametru a přiřadit mu hodnotu.

Nespecifikovaný počet parametrů

Symbol hvězdičky lze použít k definování neurčitého počtu parametrů:

Def sum(*params): result = 0 for n in params: result += n return result sumOfNumbers1 = sum(1, 2, 3, 4, 5) # 15 sumOfNumbers2 = sum(3, 4, 5, 6) # 18 tisk(součetČísel1) tisk(součetČísel2)

V tomto případě funkce součtu přebírá jeden parametr - *params , ale hvězdička před názvem parametru znamená, že ve skutečnosti místo tohoto parametru můžeme předat neurčitý počet hodnot nebo sadu hodnot. V samotné funkci pomocí cyklu for můžete tuto sadu procházet a provádět různé akce s předanými hodnotami. V tomto případě je například vrácen součet čísel.

Vrácení výsledku

Funkce může vrátit výsledek. K tomu funkce používá příkaz return následovaný návratovou hodnotou:

Def výměna(kurz_usd, peníze): výsledek = kolo(peníze/kurz_usd, 2) vrátit výsledek výsledek1 = výměna(60, 30000) tisk(výsledek1) výsledek2 = výměna(56, 30000) tisk(výsledek2) výsledek3 = výměna(65, 30 000) tisk (výsledek 3)

Protože funkce vrací hodnotu, můžeme tuto hodnotu přiřadit nějaké proměnné a pak ji použít: vysledek2 = exchange(56, 30000) .

V Pythonu může funkce vracet více hodnot najednou:

Def create_default_user(): jméno = "Tom" věk = 33 návratové jméno, věk user_name, user_age = create_default_user() print("Jméno:", uživatelské_jméno, "\t Age:", user_age)

Zde funkce create_default_user vrací dvě hodnoty: jméno a věk. Při volání funkce jsou tyto hodnoty přiřazeny v pořadí proměnným user_name a user_age a můžeme je použít.

hlavní funkce

Program může definovat mnoho funkcí. A aby bylo možné je všechny uspořádat, je dobrým zvykem přidat speciální hlavní funkci, ve které se pak nazývají další funkce:

Def main(): say_hello("Tom") usd_rate = 56 money = 30000 result = exchange(usd_rate, money) print("Bude vydáno", výsledek, "dolary") def say_hello(name): print("Dobrý den, " , jméno) def výměna(kurz_usd, peníze): výsledek = kolo(peníze/kurz_usd, 2) vrátit výsledek # Call main main()

Když jsem začal psát kapitolu o OOP, uvědomil jsem si, že jsem úplně zapomněl pokrýt tak velkou a potřebnou část Pythonu, jako jsou funkce. Toto téma je rozsáhlé a obsáhlé, proto abych příliš nenatahoval pauzy mezi lekcemi, rozhodl jsem se jej rozdělit na 2 části. Nejprve vám řeknu základy a poté podrobné funkce inženýrství funkcí Pythonu.

Funkce v Pythonu nejsou deklarovány jednoduše, ale velmi jednoduše. Zde je příklad toho nejjednoduššího:

def empty_func(): průchod
Deklarace začíná klíčovým slovem def, což, jak asi tušíte, je zkratka pro definovat. Za ním následuje název funkce. Za jménem je v závorce uveden seznam parametrů, které v tomto případě chybí.
Tělo funkce je odsazeno od dalšího řádku. Vezměte prosím na vědomí, že funkce s prázdným tělem jsou v Pythonu zakázány, takže jako tělo výše uvedené funkce se používá průchod „prázdný operátor“.
Nyní se podívejme na vážnější příklad.

Def safe_div(x, y): """Udělejte bezpečné rozdělení:-) pro zábavu a zisk""" pokud y != 0: z = x / y tisk z return z else: tisk "Yippie-kay-yay, matkaf___er!"
V tomto příkladu je několik inovací. První věc, která vás upoutá, je dokumentační řádek (docstring), který následuje bezprostředně za tělem funkce.
Obvykle tento řádek zabírá více než jeden řádek zdrojového textu (promiňte, slovní hříčka), a proto je uveden v trojitých uvozovkách. Je určen k popisu funkce, jejího účelu, parametrů atd. S touto linkou mohou pracovat všechna dobrá IDE. Můžete k němu také přistupovat ze samotného programu pomocí vlastnosti __doc__:

Vytisknout safe_div.__doc__
Tuto vlastnost (ano, ano, přesně vlastnost, v Pythonu jsou dokonce funkce vlastně třídy) je vhodné používat během relací interaktivní konzole.
>>> z ftplib import FTP >>> tisk FTP.__doc__ Třída FTP klienta.
Chcete-li vytvořit připojení, zavolejte třídu pomocí těchto argumentů: host, user, passwd, acct Toto jsou všechny řetězce a mají výchozí hodnotu "".
Poté použijte self.connect() s volitelným argumentem hostitele a portu. # zbytek udělám já :-)

Vraťme se k naší původní funkci. Jeho podstata je velmi jednoduchá, vyžaduje 2 parametry: x a y. Pokud y není 0, vydělí x y, vytiskne výsledek na obrazovku a jako výsledek vrátí svůj podíl. Výsledek funkce je vrácen pomocí příkazu return. Díky mechanismu n-tice popsanému v minulé lekci mohou funkce v Pythonu vracet mnoho objektů současně.

Pokud je dělitel stále nula, funkce zobrazí chybové hlášení. Bylo by chybou předpokládat, že v tomto případě funkce nic nevrátí. Správnější by bylo říci, že funkce vrátí „nic“ :) Jinými slovy, pokud funkce nemá příkaz return, nebo je volána bez parametrů, pak funkce vrací speciální hodnotu None. Můžete to snadno ověřit voláním něčeho jako print safe_div(10, 0).
Zde je trochu komplikovanější příklad, převzatý z prezentační zprávy Guida van Rossuma.

Obecně mějte na paměti, že parametry ve funkcích Pythonu jsou předávány odkazem. Dalším, možná netriviálním faktem, na který si budete muset zvyknout, je fakt, že samotné funkce jsou hodnotou, kterou lze přiřadit. Pokud použijeme naši funkci safe_div pro další experimenty, můžeme napsat následující kód.

Mystic_function = safe_div print mystic_function(10, 4)
To je prozatím vše, stále zbývá mnoho aspektů definování funkcí v Pythonu „přes palubu“, kterým se budeme věnovat příště.

Cvičení pro testování.
1. Na základě existující funkce pro nalezení GCM napište funkci pro nalezení LCM dvou čísel.
2. Napište tabelační rutinu pro funkci předávanou jako argument. Argumenty také určují počáteční, konečnou hodnotu a tabulkový krok.

PS mimochodem, jaká je optimální délka „lekce“? Co je lepší – velké kapitoly vydávané méně často nebo „méně je lépe, častěji?“




Nahoru