Skip to content

6. Funkcje

Image

6.1. Skrypt [fonc_01]: zakres zmiennych

Skrypt [fonc_01] przedstawia przykłady zakresu zmiennych między funkcjami:


# zakres zmiennych
def f1():
    # należy unikać używania zmiennych globalnych
    # zmienna globalna i
    global i
    i += 1
    # zmienna lokalna j
    j = 10
    print(f"f1[i,j]=[{i},{j}]")


def f2():
    # należy unikać stosowania zmiennych globalnych
    # zmienna globalna i
    global i
    i += 1
    # zmienna lokalna j
    j = 20
    print(f"f2[i,j]=[{i},{j}]")


def f3():
    # zmienna lokalna i
    i = 1
    # zmienna lokalna j
    j = 30
    print(f"f3[i,j]=[{i},{j}]")


# program główny
i = 0
j = 0
# te dwie zmienne będą znane funkcji f
# tylko wtedy, gdy funkcja ta wyraźnie zadeklaruje za pomocą instrukcji global, że chce z nich korzystać
# lub jeśli funkcja korzysta ze zmiennej globalnej wyłącznie w trybie odczytu
f1()
f2()
f3()
# j nie uległo zmianie, ale i uległo zmianie
print(f"[i,j]=[{i},{j}]")

Wyniki


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_01.py
f1[i,j]=[1,10]
f2[i,j]=[2,20]
f3[i,j]=[1,30]
[i,j]=[2,0]

Process finished with exit code 0

Uwagi:

  • Skrypt pokazuje wykorzystanie zmiennej i, zadeklarowanej jako globalna w funkcjach f1 i f2. W tym przypadku program główny oraz funkcje f1 i f2 korzystają z tej samej zmiennej i.

6.2. Skrypt [fonc_02]: zakres zmiennych

Skrypt [fonc_03] opiera się na skrypcie [fonc_02] i pokazuje, jak uniknąć stosowania zmiennych globalnych:


# zakres zmiennych
def f1(i):
    # zmienna lokalna i
    i += 1
    # zmienna lokalna j
    j = 10
    print(f"f1[i,j]=[{i},{j}]")
    # zwracamy zmodyfikowaną wartość
    return i


def f2(i):
    # zmienna lokalna i
    i += 1
    # zmienna lokalna j
    j = 20
    print(f"f2[i,j]=[{i},{j}]")
    # zwracamy zmodyfikowaną wartość
    return i


def f3():
    # zmienna lokalna i
    i = 1
    # zmienna lokalna j
    j = 30
    print(f"f3[i,j]=[{i},{j}]")


# program główny
i = 0
j = 0
# te dwie zmienne będą znane funkcji f
# tylko wtedy, gdy funkcja ta wyraźnie zadeklaruje za pomocą instrukcji global, że chce z nich korzystać
# lub jeśli funkcja korzysta ze zmiennej globalnej wyłącznie w trybie odczytu
i = f1(i)
i = f2(i)
f3()
# j nie uległo zmianie, ale i uległo zmianie
print(f"[i,j]=[{i},{j}]")

Komentarze:

  • wiersze 2, 12: zamiast deklarowania jako globalna, zmienna [i] jest przekazywana jako parametr do funkcji f1 i f2;
  • wiersze 9, 19: funkcje f1 i f2 zwracają zmienioną zmienną [i] do programu głównego. Program ten pobiera ją w wierszach 36 i 37;

Wyniki


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_02.py
f1[i,j]=[1,10]
f2[i,j]=[2,20]
f3[i,j]=[1,30]
[i,j]=[2,0]

Process finished with exit code 0

6.3. Skrypt [fonc_03]: zakres zmiennych

Skrypt [fonc_03] ilustruje specyfikę zmiennych używanych zarówno w funkcji, jak i w kodzie wywołującym tę funkcję, w zależności od tego, czy w funkcji zmienna ta jest wykorzystywana wyłącznie do odczytu, czy też nie.


def f1():
    # tutaj zmienna globalna i jest znana
    print(f"[f1] i={i}")
    # tutaj znana jest zmienna globalna j
    print(f"[f1] j={j}")


def f2():
    # tutaj zmienna globalna i nie jest znana
    # ponieważ funkcja f2 definiuje zmienną lokalną o tej samej nazwie
    # wówczas to ona ma pierwszeństwo
    try:
        # próba wyświetlenia zmiennej lokalnej i zdefiniowanej dalej
        print(f"[f2] i={i}")
        # kolejna instrukcja sprawia, że i staje się zmienną lokalną funkcji f2
        i = 7
    except BaseException as erreur:
        print(f"[f2] erreur={erreur}")


def f3():
    # tutaj zmienna globalna i nie jest znana
    # ponieważ funkcja f3 definiuje zmienną lokalną o tej samej nazwie
    # to właśnie ona ma pierwszeństwo

    # poniższa instrukcja definiuje i jako zmienną lokalną
    i = 7
    # wyświetlanie – tutaj i jest znana
    print(f"[f3] i={i}")


# main -----------
# zmienne globalne dla funkcji
i = 10
j = 20
# wywołanie funkcji f1
f1()
print(f"[main] i={i}, j={j}")
# wywołanie funkcji f2
f2()
print(f"[main] i={i}")
# wywołanie funkcji f3
f3()
print(f"[main] i={i}")

Uwagi

  • wiersz 34: kod główny definiuje zmienną [i];
  • wiersze 1–5: funkcja f1 również wykorzystuje zmienną [i], nie nadając jej jednak żadnej wartości. Jest to odczyt zmiennej [i]. W tym przypadku wykorzystywana jest zmienna [i] z kodu wywołującego, wiersz 34;
  • wiersze 8–18: funkcja f2 również wykorzystuje zmienną [i], ale nadaje jej wartość w wierszu 16. Nadanie wartości zmiennej [i] w funkcji f2 automatycznie sprawia, że [i] staje się zmienną lokalną funkcji [f2]. Ta ostatnia „maskuje” zatem zmienną [i] z kodu wywołującego;
  • wiersz 14: operacja zapisu zmiennej lokalnej [i] zakończy się niepowodzeniem, ponieważ nie ma ona wartości w momencie dotarcia do wiersza 14. Otrzymuje ona swoją wartość w wierszu 16. Wystąpi wyjątek. Z tego powodu wiersz 14 umieszczono w bloku try / catch;
  • wiersze 21–29: funkcja f3 wykonuje to samo co funkcja f2, ale wcześniej definiuje swoją zmienną lokalną [i];

Wyniki


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_03.py
[f1] i=10
[f1] j=20
[main] i=10, j=20
[f2] erreur=local variable 'i' referenced before assignment
[main] i=10
[f3] i=7
[main] i=10

Process finished with exit code 0

6.4. Skrypt [fonc_04]: sposób przekazywania parametrów

Skrypt wygląda następująco:


# funkcja f1
def f1(a):
    a = 2


# funkcja f2
def f2(a, b):
    a = 2
    b = 3
    return a, b


# ------------------------ main
x = 1
f1(x)
print(f"x={x}")
(x, y) = (-1, -1)
(x, y) = f2(x, y)
print(f"x={x}, y={y}")

Wyniki

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_04.py
x=1
x=2, y=3

Process finished with exit code 0

Uwagi:

  • w języku Python wszystko jest obiektem. Niektóre obiekty określa się jako „niezmienne” (ang. immutable): nie można ich modyfikować. Dotyczy to liczb, ciągów znaków i krotek. Gdy obiekty języka Python są przekazywane jako parametry do funkcji, przekazywane są ich odwołania, chyba że obiekty te są „niezmienne” – w takim przypadku przekazywana jest wartość obiektu;
  • funkcje f1 (wiersz 2) i f2 (wiersz 7) mają na celu zilustrowanie przekazywania parametru wyjściowego. Chcemy, aby rzeczywisty parametr funkcji został przez nią zmodyfikowany;
  • wiersze 2–3: funkcja f1 modyfikuje swój parametr formalny a. Chcemy sprawdzić, czy parametr rzeczywisty również zostanie zmodyfikowany;
  • wiersze 14–15: parametrem rzeczywistym jest x=1. Wiersz 2 wyników pokazuje, że parametr rzeczywisty nie ulega zmianie. Zatem parametr rzeczywisty x i parametr formalny a to dwa różne obiekty;
  • wiersze 8–10: funkcja f2 modyfikuje swoje parametry formalne a i b, a następnie zwraca je jako wyniki;
  • wiersze 17–18: do funkcji f2 przekazywane są parametry wejściowe (x, y), a wynik funkcji f2 jest zapisywany w (x, y). Wiersz 3 wyników pokazuje, że parametry wejściowe (x, y) uległy zmianie.

Wniosek jest taki, że gdy obiekty „niezmienne” są parametrami wyjściowymi, muszą one stanowić część wyników zwracanych przez funkcję.

6.5. Skrypt [fonc_05]: kolejność zapisywania funkcji w skrypcie

Skrypt [fonc_05] pokazuje, że nie można wywołać funkcji, jeśli nie pojawiła się ona wcześniej w kodzie:


# ------------------------ main
print(f2(100, 200))

# funkcja f1
def f1(a):
    return a + 10


# funkcja f2
def f2(a, b):
    return f1(a + b)

Uwagi

  • wiersz 2 spowoduje błąd, ponieważ wykorzystuje funkcję f2, która nie została jeszcze zdefiniowana w skrypcie;

Wyniki

1
2
3
4
5
6
7
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_05.py
Traceback (most recent call last):
  File "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_05.py", line 2, in <module>
    print(f2(100, 200))
NameError: name 'f2' is not defined

Process finished with exit code 1

6.6. Skrypt [fonc_06]: kolejność zapisywania funkcji w skrypcie

Skrypt [fonc_06] pokazuje, że to, co dotyczy kodu wywołującego, nie ma zastosowania w przypadku funkcji:


# funkcja f2
def f2(a, b):
    return f1(a + b)


# funkcja f1
def f1(a):
    return a + 10


# ------------------------ main
print(f2(100, 200))

Uwagi

  • wiersz 3: funkcja [f2] wykorzystuje funkcję [f1] zdefiniowaną w dalszej części skryptu. Nie powoduje to jednak błędu. Można zatem wywnioskować, że kolejność definiowania funkcji w skrypcie w języku Python nie ma znaczenia;

Wyniki

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_06.py
310

Process finished with exit code 0

6.7. Skrypt [fonc_07]: wykorzystanie modułów

Skrypt [fonc_07] pokazuje, jak wyodrębnić funkcje do modułu.

Image

Funkcje, które można ponownie wykorzystać, izolujemy w module. Zamiast przenosić je z jednego skryptu do drugiego:

  • umieszczamy je w osobnym pliku, który deklarujemy w określony sposób;
  • skrypty, które potrzebują tych funkcji, „importują” moduł, który je zawiera;

Skrypt [fonctions_module_01] wygląda następująco:


# funkcja f2
def f2(a, b):
    return f1(a + b)

# funkcja f1
def f1(a):
    return a + 10

Aby funkcje skryptu [fonctions_module_01] mogły być wywoływane przez inne skrypty, istnieje kilka sposobów. Różnią się one w zależności od tego, czy skrypt jest uruchamiany wewnątrz [PyCharm], czy nie.

W skrypcie [PyCharm] importowane moduły są wyszukiwane w określonych folderach o nazwie [Sources Root]. Istnieją dwa sposoby, aby przekształcić folder w [Sources Root]:

Image

  • w pliku [4] folder zmienił kolor;

Po tej operacji folder [fonctions/modules] jest rozpoznawany jako folder źródłowy. W skrypcie można wówczas wpisać:

from fonctions_module_01 import f2

Aby zaimportować / użyć funkcji f2 zdefiniowanej w module [fonctions_module_01.py].

Inną metodą jest skorzystanie z właściwości projektu:

  • powyżej sekwencja [1-6] pozwala przekształcić folder [shared] w folder, w którym będą przechowywane moduły do zaimportowania;

Na razie nie będziemy definiować żadnego folderu o nazwie [Sources Root] poza katalogiem głównym projektu:

Image

Po wykonaniu tej czynności możemy napisać następujący skrypt [fonc-07]:


# korzystanie z modułów
import sys

# ------------------------ main
print(f"Python path={sys.path}")
from fonctions.shared.fonctions_module_01 import f2

print(f2(100, 200))
  • wiersz 2: importujemy obiekt [sys], aby móc w wierszu 5 wykorzystać jego atrybut [path], który zwraca tzw. [Python Path]: listę folderów, które zostaną przeszukane w poszukiwaniu zaimportowanych modułów;
  • wiersz 6: importujemy funkcję f2 z modułu [fonctions_module_01]. Aby wskazać ten moduł, używa się ścieżki prowadzącej od katalogu głównego projektu do modułu. W Pycharmie katalog główny projektu zawsze znajduje się wśród katalogów przeszukiwanych podczas wyszukiwania modułu importowanego w skrypcie. Katalog ten jest zatem częścią [Python Path] projektu. Właśnie to pozwoli nam sprawdzić wiersz 5;
  • wiersz 6: gdybyśmy opisywali ścieżkę prowadzącą od katalogu głównego projektu do folderu [fonctions_module_01], zapisalibyśmy [fonctions/shared/fonctions_module_01]. W ścieżce modułu znak / zastępuje się kropką. Zapisujemy zatem [fonctions.modules.fonctions_module_01];
  • po wierszu 6 znana jest funkcja f2. Wykorzystujemy ją w wierszu 8;

Wyniki


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_07.py
Python path=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions\\shared', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages']
310

Process finished with exit code 0

Powyżej:

  • zaznaczone na zielono widać, że katalog główny projektu jest częścią [Python Path];
  • zaznaczone na żółto widać, że folder skryptu, który został uruchomiony, również jest częścią [Python Path];
  • pozostałe elementy [Python Path] pochodzą bezpośrednio z folderu instalacyjnego Pythona;

Co się dzieje, gdy nie używa się PyCharm do uruchomienia [fonc-07]?

Image

Image

  • w przypadku pliku [1] uruchamiany jest skrypt [fonc-07]. Znajdujemy się w folderze [fonctions];
  • w pliku [2] widać, że katalog wykonawczy jest częścią pliku [Python Path]. Tak jest zawsze. Widać również, że katalog główny [C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020] nie jest częścią pliku [Python Path];
  • w [3] interpreter języka Python zgłasza, że nie może znaleźć modułu [fonctions];

Aby znaleźć importowany moduł [fonctions.shared.fonctions_module_01], interpreter języka Python przeszukuje katalogi należące do [Python Path] w poszukiwaniu podkatalogu o nazwie [fonctions]. Nie znajduje go nigdzie. W rzeczywistości podfolder [fonctions] znajduje się w folderze [C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020], który nie jest częścią folderu [Python Path].

Skrypt [fonc-08] przedstawia możliwe rozwiązanie tego problemu.

6.8. Skrypt [fonc_08]: dodawanie folderów do [Python Path]

Możliwe jest programowe zmodyfikowanie pliku [Python Path], jak pokazuje skrypt [fonc-08]:


# korzystanie z modułów
import os
import sys

# katalog skryptu
script_dir = os.path.dirname(os.path.abspath(__file__))
# Ścieżka Python przed modyfikacją
print(f"Python path avant={sys.path}")
# dodajemy katalog [shared] do ścieżki Python Path
sys.path.append(f"{script_dir}/shared")
# ścieżka Python Path po modyfikacji
print(f"Python path après={sys.path}")

# import f2
from fonctions_module_01 import f2

# ------------------------ main
print(f2(100, 200))

Uwagi

  • wiersz 4: zmienna specjalna [__file__] to nazwa skryptu, który jest wykonywany. W zależności od kontekstu wykonania nazwa ta może być bezwzględna (Pycharm) lub względna (konsola). Funkcja [os.path.abspath] zwraca bezwzględną nazwę pliku, którego nazwę jej przekazano. Funkcja [os.path.dirname] zwraca bezwzględną nazwę folderu zawierającego plik, którego nazwę jej przekazano;
  • wiersz 10: [sys.path] to tablica zawierająca nazwy folderów, które mają być przeszukiwane podczas wyszukiwania modułu. Do tej tablicy dodaje się katalog główny projektu zdefiniowany w wierszu 4;
  • wyświetla się [Python Path] przed (wiersz 8) i po (wiersz 12) modyfikacji;
  • wiersz 15: importujemy moduł [fonctions_module_01], który zawiera funkcję f2;

Wykonanie w PyCharm daje następujące wyniki:


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_08.py
Python path avant=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions\\shared', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages']
Python path après=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions\\shared', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions/shared']
310

Process finished with exit code 0
  • wiersz 3: widać, że plik [shared] występuje dwukrotnie w pliku [Python Path]. Można tego uniknąć, ale w tym przypadku nie stanowi to przeszkody;
  • wiersz 4: funkcja f2 została pomyślnie wykonana;

Teraz uruchommy plik [fonc-08] w terminalu:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\fonctions>python fonc_08.py
Python path avant=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages']
Python path après=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions/shared']
310
  • wiersz 2: podobnie jak poprzednio, folder [shared] nie znajduje się w [Python Path];
  • wiersz 3: teraz już tam jest;
  • wiersz 4: znaleziono funkcję f2;

6.9. Skrypt [fonc_09]: deklaracja typów parametrów

Skrypt [fonc_09] pokazuje, że można zadeklarować typy parametrów funkcji, a także typ wyniku. Jednak taka deklaracja służy wyłącznie do celów dokumentacji funkcji. Interpreter języka Python nie sprawdza, czy rzeczywiste parametry funkcji rzeczywiście mają oczekiwany typ. Natomiast Pycharm sygnalizuje niezgodności typów między parametrami rzeczywistymi a formalnymi. Już ten jeden powód wystarcza, by deklaracje typów stały się niezbędne.

Skrypt wygląda następująco:


# funkcja z określeniem typów parametrów
# służy to wyłącznie jako dokumentacja, ponieważ interpreter Pythona nie bierze tego pod uwagę


def show(param: int) -> int:
    print(f"param={param}, type(param)={type(param)}")
    return param + 1


# main -------------------------
print(show(4))
show("xyz")

Uwagi:

  • wiersz 5: deklarujemy, że parametr formalny [param] jest typu [int], a wynik funkcji jest również typu [int];
  • wiersz 11: parametr rzeczywisty funkcji [show] ma właściwy typ;
  • wiersz 12: rzeczywisty parametr funkcji [show] nie ma właściwego typu;

Wyniki

C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_09.py
param=4, type(param)=<class 'int'>
5
param=xyz, type(param)=<class 'str'>
Traceback (most recent call last):
  File "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_09.py", line 11, in <module>
    show("xyz")
  File "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_09.py", line 6, in show
    return param + 1
TypeError: can only concatenate str (not "int") to str

Process finished with exit code 1
  • wiersz 10: typ parametru [param] to [str]. Gdy pojawia się ten komunikat, jesteśmy już w kodzie funkcji [show]. Interpreter języka Python zaakceptował zatem, że rzeczywisty parametr funkcji [show] ma typ [str];
  • wiersz 7 kodu powoduje wyjątek odzwierciedlony w wierszach 4–10 wyników;

PyCharm wskazuje jednak na występowanie anomalii:

Image

W przypadku kodu [1] błędne wywołanie zostało zaznaczone w kodzie PyCharm.

6.10. Skrypt [fonc_10]: parametry nazwane

Aby przekazać parametry do funkcji, można użyć nazw jej parametrów formalnych. W takim przypadku nie ma konieczności zachowania kolejności parametrów formalnych:


# parametry rzeczywiste można określać za pomocą ich nazw formalnych
def f(x, y):
    return x + y


# main
print(f(y=10, x=3))

Uwagi

  • wiersz 2: funkcja f ma dwa parametry formalne x i y;
  • wiersz 7: podczas wywołania funkcji f można używać nazw parametrów formalnych. Praktyka ta może być przydatna przynajmniej w dwóch przypadkach:
    • funkcja ma wiele parametrów, z których większość ma wartości domyślne. Podczas wywołania powyższa technika pozwala zainicjować tylko te parametry, dla których nie chcemy używać wartości domyślnych;
    • jeśli parametry formalne mają znaczące nazwy, to użycie nazwanych parametrów podczas wywołania funkcji poprawia czytelność kodu;

Wyniki

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_10.py
13

Process finished with exit code 0

6.11. Skrypt [fonc_11]: funkcja rekurencyjna

Skrypt [fonc_11] jest przykładem funkcji rekurencyjnej (która wywołuje samą siebie):


# funkcja rekurencyjna
def fact(i: int) -> int:
    # silnia(1) wynosi 1
    # funkcja rekurencyjna musi w pewnym momencie się zakończyć
    if i == 1:
        return 1
    else:
        # faktorial(i) = i * faktorial(i-1)
        return i * fact(i - 1)


# ---------- main
print(f"fact(8)={fact(8)}")

Komentarze

  • wiersze 1–9: funkcja silni;
  • wiersz 9: funkcja [factorielle] wywołuje samą siebie;
  • wiersze 5–6: funkcja rekurencyjna musi zawsze zakończyć działanie, gdy spełniony zostanie warunek, w przeciwnym razie dochodzi do rekurencji nieskończonej;

Wyniki

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_11.py
fact(8)=40320

Process finished with exit code 0

6.12. Skrypt [fonc_12]: funkcja rekurencyjna

Funkcja [fonc_12] zawiera więcej szczegółów na temat działania rekurencji:


# funkcja rekurencyjna
# zachowanie parametru j


def fact(i: int, j: int) -> int:
    # zakończenie funkcji rekurencyjnej
    if i == 1:
        print(f"j={j}")
        return 1
    else:
        # manipulujemy zmienną j
        j += 1
        print(f"avant fact j={j}")
        # rekurencja
        f = fact(i - 1, j)
        print(f"après fact j={j}")
        # wynik
        return i * f


# ---------- main
print(f"fact(8)={fact(8, 0)}")

Komentarze

  • wiersz 5: nadal zajmujemy się funkcją silni. Dodajemy do niej parametr [j];
  • wiersz 12: zmienna j jest regularnie zwiększana przy każdym obliczeniu silni. Wyświetla się jej wartość przed (wiersz 12) i po (wiersz 16) rekurencji (wiersz 15);

Wyniki

C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_12.py
avant fact j=1
avant fact j=2
avant fact j=3
avant fact j=4
avant fact j=5
avant fact j=6
avant fact j=7
j=7
après fact j=7
après fact j=6
après fact j=5
après fact j=4
après fact j=3
après fact j=2
après fact j=1
fact(8)=40320

Process finished with exit code 0
  • wiersze 2–8: widać, że wartość [j] rośnie tak długo, jak długo trwa rekurencja, aż do spełnienia warunku, w którym rekurencja się zatrzymuje. Od tego momentu powrót z wywołań funkcji [fact] odbywa się w odwrotnej kolejności niż wywołania;
  • wiersze 10–16: te wyświetlenia odzwierciedlają kolejne powroty z wywołania funkcji silni. Zmienna [j] powraca do swoich wartości, aż do wartości początkowej 1;