PPy3/Funkcje

Z Brain-wiki

Funkcje

Gdy raz wymyślimy jakiś sprytny algorytm, to nie ma co go za każdym razem odkrywać na nowo. Żeby było łatwiej pisać raz, a korzystać wielokrotnie - wymyślono funkcje.

Definiowanie funkcji

def funkcja(x, y):
    BLOK INSTRUKCJI
  • Definicja funkcji to instrukcja złożona, podobna w strukturze do tych, które już znamy - instrukcji warunkowej i pętli.
  • Wykonanie definicji funkcji nie wiąże się z natychmiastowym wykonaniem zawartego w niej bloku.
  • W definicji funkcji (przykładowej) x i y to parametry formalne; nie mają one w tym momencie określonych wartości, są miejscami do wypełnienia konkretnymi wartościami, gdy funkcję postanowimy użyć. Może ich być (niemal) dowolnie wiele, w tym — zero. W tym ostatnim przypadku piszemy pustą parę nawiasów.
  • Wykonanie definicji funkcji polega na nadaniu znaczenia jej nazwie, która odtąd będzie oznaczała ciąg instrukcji stanowiących wewnętrzny blok.
  • Użycie czyli wywołanie funkcji to wyrażenie postaci
funkcja(x, y)

gdzie x i y mają już konkretne wartości (w tym miejscu mogą stać również wyrażenia złożone), które będą użyte w instrukcjach stanowiących definicję.

  • W definicji na ogół występuje (raz lub więcej razy) słowo (instrukcja) return. Oznacza ono, że w tym miejscu wykonanie funkcji się kończy - funkcja powraca. Wartość wyrażenia po słowie return stanowi wynik zwracany przez funkcję - czyli wartość wyrażenia, będącego wywołaniem funkcji.
  • Jeśli return nie ma, albo nie ma po nim wartości zwracanej, albo wykonanie funkcji kończy się ,,wypadnięciem" przez koniec bloku, wartością zwracaną jest None — która w zasadzie do niczego się specjalnie nie nadaje, poza sprawdzeniem, czy mamy do czynienia z właśnie tą wartością.
  • funkcja w Pythonie (i większości języków programowania) to coś nieco podobnego do funkcji (odwzorowania) w matematyce, ale to nie jest to samo pojęcie:
    • w Pythonie wywołanie funkcji może mieć skutki uboczne;
    • wynik funkcji, w tym skutki uboczne, może zależeć nie tylko od argumentów wywołania.

Skutkami ubocznymi wywołania nazywamy jakiekolwiek efekty tej instrukcji, które nie sprowadzają się do wartości zwracanej. A więc, zmianę wartości innych zmiennych, czy np. wysłanie jakichś danych siecią, lub wypisanie czegoś na ekran.

  • Funkcja może być wartością zmiennej; inaczej mówiąc, funkcja może być znana pod dodatkowymi nazwami (aliasami), może być włożona do listy na którąś z jej pozycji itp.
ff = funkcja
...
wynik = ff(x)
  • ,,Oryginalna" nazwa funkcji, czyli występująca w jej definicji, jest jednak w pewnym sensie uprzywilejowana.
  • Zmienne (nazwy) powołane do życia wewnątrz funkcji są lokalne; ich wartości ,,żyją" tylko póki wykonuje się funkcja.
  • Zmienne lokalne powielające nazwy ,,zewnętrzne" względem funkcji przesłaniają te zewnętrzne. Chyba, że użyjemy deklaracji global:
x = 1
def podwojx():  # to nie zadziała:
    x = x * 2
podwojx()
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-3-6edd575e5cda> in <module>()
----> 1 podwojx()

<ipython-input-1-1da635f42ffa> in podwojx()
      1 def podwojx():
----> 2     x *= 2
      3 

UnboundLocalError: local variable 'x' referenced before assignment

Jak widzimy, komunikat o błędzie pojawił się dopiero w wyniku wywołania błędnej funkcji.

x = 1
def podwojx():  # to zadziała - co nie znaczy, że jest mądre...
    global x
    x = x * 2
podwojx()
print x
 2

Należy unikać takich sztuczek, jak powyżej; jeśli już, to:

def podwoj(x):
    return 2 * x
liczba = 1
print podwoj(liczba)
 2

Wskazówka: każde użycie deklaracji global w programie powinno być dobrze uzasadnione. Jeżeli używamy jej więcej niż sporadycznie, to coś jest nie tak.

Funkcja może być

  • zeroargumentowa — wówczas zarówno w definicji, jak i w wywołaniu po nazwie występuje pusta para nawiasów,
  • jednoargumentowa (jak powyższa podwoj),
  • dwuargumentowa (jak przykładowa),
  • trzy-, cztero-, ... itd.
  • o zmiennej liczbie argumentów, z wartościami domyślnymi dla brakujących:
def dodaj(x, y=1):
    return x + y
dodaj(2, 3)
 5
dodaj(2)
 3

Parametry z wartościami domyślnymi muszą występować na końcu listy parametrów.

  • o zmiennej liczbie argumentów, ale bez wartości domyślnych, np.:
def ile(*args):
    return len(args)

Działa to tak: '*' postawiona przed nazwą argumentu oznacza, że wszystkie (ew. dalsze) argumenty zostaną zebrane w jedną listę, która dostępna jest wewnątrz funkcji pod nazwą, jaką postawiliśmy po gwiazdce. W powyższym przykładzie, funkcja ile zwraca jako swój wynik po prostu liczbę argumentów, z jakimi funkcja została wywołana. Operator '*' nie ma tu wiele wspólnego z mnożeniem. Odróżniamy go od mnożenia po tym, że po jego lewej stronie nie stoi żaden czynnik. Ten sam operator może wystąpić również w roli poniekąd odwrotnej, np.:

lista = [*range(5)]
print(*lista)
 0 1 2 3 4

W pierwszej linii, gwiazdka niejako ,,rozwija" sekwencję range(5) w ciąg wartości (kolejnych liczb) wstawionych pod operator [...]; w linii drugiej, lista jest również ,,rozpakowana" w ciąg oddzielnych argumentów wywołania funkcji print — sprawdź, że wynik print(lista) wyglądałby nieco inaczej.

Ćwiczenia

1. Napisz funkcję pierwiastki(a, b, c), która wypisuje na ekran (za pomocą wywołania print) pierwiastki równania kwadratowego o wspołczynnikach a, b i c: a * x**2 + b * x + c == 0. W przypadku, gdy pierwiastków nie ma, wypisuje komunikat Równanie nie ma pierwiastków. W przypadku, gdy a=0, wypisuje komunikat Błędne dane: a==0 i kończy działanie.

2. Napisz analogiczną funkcję, która zamiast bezpośrednio wypisywać rozwiązania, zwraca je jako wynik swojego działania. Wypisaniem ich na ekran zajmuje się odrębna instrukcja. W przypadku, gdy pierwiastków nie ma, zwraca wartość None. Nie sprawdzaj warunku a!=0 - co się dzieje, gdy nie jest spełniony przez argumenty wywołania funkcji?

3. Napisz funkcję silnia(n), która oblicza i zwraca wartość silni: n! := 1 * 2 * 3 * ... (n - 1) * n. Uwzględnij, że przyjmuje się że 0! = 1, natomiast dla liczb ujemnych silnia nie jest określona - co można zasygnalizować zwracając jako wynik None. Użyj pętli for i funkcji range.

4. Funkcję silnia można zdefiniować w sposób rekurencyjny - za pomocą następujących warunków:

n! == None dla n < 0
0! == 1
n! == n * (n - 1)!

Napisz kod funkcji silnia(n) realizujący powyższą definicję. Porównaj działanie tego kodu z realizacją tej funkcji z poprzedniego przykładu dla dużych wartości n.


poprzednie | strona główna | dalej

RobertJB (dyskusja) 16:10, 30 cze 2016 (CEST)