Człowiek po prostu przykro
Transkrypt
Człowiek po prostu przykro
Wstęp do programowania
Wykład 7 Funkcje
Janusz Szwabiński
Plan wykładu:
Zastosowania funkcji
Funkcje wbudowane
Funkcje biblioteczne
Funkcje użytkownika
Zastosowania funkcji
Z punktu widzenia programowania funkcje to nazwane sekwencje instrukcji. Tworzymy je z następujących
powodów:
nadawanie nazw pewnym sekwencjom instrukcji może uczynić kod bardziej czytelnym i skrócić czas
usuwania błędów
dzięki funkcjom kod źródłowy programu jest z reguły krótszy, ponieważ eliminują one potrzebę
powtarzania tych samych sekwencji poleceń
program jest łatwiejszy w utrzymaniu ponieważ dana sekwencja wprowadzana jest tylko raz, w
definicji funkcji, ewentualnych zmian dokonujemy tylko w jednym miejscu
podzielenie programu na części o dobrze zdefiniowanych funkcjonalnościach powoduje, że możemy
każdą część debuggować z osobna
dobrze zaprojektowane funkcje mogą być używane ponownie przez inne programy
Funkcje wbudowane
Interpreter Pythona oferuje pewną liczbę funkcji wbudowanych, czyli zawsze dostępnych dla programisty.
Część z nich stosowaliśmy już na poprzednich wykładach intuicyjnie, bez wnikania w ich naturę. Z innych
będziemy korzystać niezmiernie rzadko lub wcale (przynajmniej w ramach tego wykładu). Pełną listę tych
funkcji można znaleźć pod adresem https://docs.python.org/2/library/functions.html
(https://docs.python.org/2/library/functions.html).
Polecenia wykorzystywane do konwersji typów to właśnie przykład funkcji wbudowanych, z których już
korzystaliśmy:
In [1]:
int('32')
Out[1]:
32
In [2]:
int('Hello')
-------------------------------------------------------------------------ValueError
Traceback (most recent ca
ll last)
<ipython-input-2-3f8bc6d8fcab> in <module>()
----> 1 int('Hello')
ValueError: invalid literal for int() with base 10: 'Hello'
In [3]:
int(-2.3)
Out[3]:
-2
In [4]:
float(32)
Out[4]:
32.0
In [5]:
str(32)
Out[5]:
'32'
Funkcje do tworzenia typów złożonych również należą do tej grupy:
In [6]:
dict(a=1,b=2,c=3)
Out[6]:
{'a': 1, 'b': 2, 'c': 3}
In [7]:
list('anyway')
Out[7]:
['a', 'n', 'y', 'w', 'a', 'y']
Do dyspozycji mamy kilka funkcji matematycznych:
In [8]:
abs(-1)
Out[8]:
1
In [9]:
round(2.3)
Out[9]:
2
In [10]:
pow(2,3)
Out[10]:
8
In [11]:
oct(15)
Out[11]:
'0o17'
In [12]:
bin(3)
Out[12]:
'0b11'
In [13]:
hex(255)
Out[13]:
'0xff'
Przy pomocy funkcji wbudowanych wczytujemy również dane z klawiatury:
In [14]:
s = input('--->')
--->python
In [15]:
print(s)
python
In [16]:
l = input('Podaj liczbę: ')
Podaj liczbę: 4
In [22]:
l**2
-------------------------------------------------------------------------TypeError
Traceback (most recent ca
ll last)
<ipython-input-22-0c9562729bb4> in <module>()
----> 1 l**2
TypeError: unsupported operand type(s) for ** or pow(): 'str' and
'int'
In [23]:
type(l)
Out[23]:
str
In [18]:
int(l)**2
Out[18]:
16
Ogólnie rzec biorąc, funkcja oczekuje podania na wejściu argumentu lub listy argumentów i produkuje jakiś
wynik na wyjściu (w dalszej części wykładu omówimy kilka wyjątków). Wynik ten możemy przypisać
zmiennej, lub bezpośrednio wyprowadzić na ekran.
Funkcje biblioteczne
Instalując dystrybucję Pythona, instalujemy również jego bibilotekę standardową, która zawiera bardzo dużo
modułów z funkcjami rozszerzającymi podstawowe możliwości języka. Pełna dokumentacja biblioteki
znajduje się pod adresem https://docs.python.org/3/ (https://docs.python.org/3/)
Aby skorzystać z funkcji zdefiniowanej w bibliotece standardowej (i nie będącej funkcją wbudowaną), musimy
załadować moduł, w którym jest ona zdefiniowana. Najbezpieczniejszy sposób zaimportowania definicji (o
tym więcej w dalszej części kursu) ma składnię
import nazwa_modułu
Na przykład, jeśli chcemy korzystać z funkcji matematycznych, importujemy moduł math z biblioteki
standardowej:
In [24]:
import math
W wyniku wykonania tego polecenia interpreter tworzy obiekt modułu o nazwie math
In [25]:
print(math)
<module 'math' (built-in)>
Moduł zawiera szereg definicji. Aby je przeglądnąć, możemy wywołać na nim funkcję dir:
In [26]:
dir(math)
Out[26]:
['__doc__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'acos',
'acosh',
'asin',
'asinh',
'atan',
'atan2',
'atanh',
'ceil',
'copysign',
'cos',
'cosh',
'degrees',
'e',
'erf',
'erfc',
'exp',
'expm1',
'fabs',
'factorial',
'floor',
'fmod',
'frexp',
'fsum',
'gamma',
'hypot',
'isfinite',
'isinf',
'isnan',
'ldexp',
'lgamma',
'log',
'log10',
'log1p',
'log2',
'modf',
'pi',
'pow',
'radians',
'sin',
'sinh',
'sqrt',
'tan',
'tanh',
'trunc']
Dostęp do poszczególnych funkcji i stałych odbywa się w następujący sposób:
nazwa_modułu.nazwa_funkcji
In [27]:
angle = math.pi
In [28]:
math.sin(math.pi/2)
Out[28]:
1.0
Argumentem funkcji (ogólnie, nie tylko matematycznej) może być wyrażenie, w skład którego wchodzą inne
funkcje:
In [29]:
p = '3.14'
math.sin(p)
-------------------------------------------------------------------------TypeError
Traceback (most recent ca
ll last)
<ipython-input-29-54671218241a> in <module>()
1 p = '3.14'
----> 2 math.sin(p)
TypeError: a float is required
In [30]:
math.sin(float(p))
Out[30]:
0.0015926529164868282
In [31]:
math.sin(math.acos(0.0))
Out[31]:
1.0
Funkcje użytkownika
Do tej pory rozważaliśmy jedynie gotowe funkcje, albo wbudowane w interpreter, albo zawarte w bibliotece
standardowej. Python oczywiście oferuje nam również możliwość definiowania własnych funkcji. Składnia
takiej definicji jest następująca:
def nazwa_funckji(arg1,arg2,arg3,...):
<instrukcje>
return wynik
Słowo kluczowe def wprowadza definicję funkcji. Po nim następuje nazwa funkcji oraz lista parametrów
formalnych otoczonych nawiasami okrągłymi. Parametry formalne to po prostu argumenty funkcji. Może ich
być dowolna liczba, również 0. Pierwszy wiersz definicji kończy się dwukropkiem, o którym wiemy już, że
otwiera blok zawierający sekwencję instrukcji. Wszystkie polecenia w tym bloku powinny być wcięte w
stosunku do słowa kluczowego def. Kolejne słowo kluczowe, return zwraca wynik działania funkcji i
przekazuje kontrolę do części programu, z poziomu którego funkcja zostanie później wywołana. Jeżeli
efektem działania funkcji ma być np. wypisanie czegoś na ekranie, zamiast wyliczenia konkretnego wyniku,
wiersz zawierający return możemy pominąć. Funkcja zwróci wówczas wartość specjalną None.
Definiowanie funkcji
Rozważmy przykład:
In [32]:
def print_lyrics():
print("Jak człowiek wierzy w siebie,")
print("to cała reszta to betka,")
print("nie ma takiej rury na świecie,")
print("której nie można odetkać")
W wyniku wykonania definicji tworzony jest obiekt funkcji o odpowiedniej nazwie:
In [33]:
print(print_lyrics)
<function print_lyrics at 0x7ffbd835d158>
In [34]:
type(print_lyrics)
Out[34]:
function
Funkcje zdefiniowane przez użytkownika wywołujemy tak samo, jak funkcje wbudowane:
In [35]:
print_lyrics()
Jak człowiek wierzy w siebie,
to cała reszta to betka,
nie ma takiej rury na świecie,
której nie można odetkać
In [36]:
test = print_lyrics()
Jak człowiek wierzy w siebie,
to cała reszta to betka,
nie ma takiej rury na świecie,
której nie można odetkać
In [37]:
print(test)
None
Raz zdefiniowana funkcja może zostać wykorzystana w innych definicjach:
In [38]:
def repeat_lyrics(n):
for i in range(n):
print_lyrics()
print("****")
Nowa funkcja oczekuje jednego argumentu, dlatego wywołanie jej bez argumentów spowoduje błąd:
In [39]:
repeat_lyrics()
-------------------------------------------------------------------------TypeError
Traceback (most recent ca
ll last)
<ipython-input-39-cd0a4288b2d7> in <module>()
----> 1 repeat_lyrics()
TypeError: repeat_lyrics() missing 1 required positional argument:
'n'
Natomiast wywołanie funkcji z odpowiednim argumentem zadziała w oczekiwany sposób:
In [40]:
repeat_lyrics(3)
Jak człowiek wierzy w siebie,
to cała reszta to betka,
nie ma takiej rury na świecie,
której nie można odetkać
****
Jak człowiek wierzy w siebie,
to cała reszta to betka,
nie ma takiej rury na świecie,
której nie można odetkać
****
Jak człowiek wierzy w siebie,
to cała reszta to betka,
nie ma takiej rury na świecie,
której nie można odetkać
****
Dzięki własnościom Pythona proste funkcje potrafią być bardzo uniwersalne. Rozważmy np. funkcję, która
dodaje do siebie dwa argumenty:
In [41]:
def dodaj(x,y):
a=x+y
return a
Funkcja ta zadziała dla liczb całkowitych, rzeczywistych, zespolonych, łańcuchów znaków i list:
In [42]:
print(dodaj(2,7)) #całkowite
print(dodaj(3.14,7.2)) #rzeczywiste
print(dodaj(1+1j,2+3j)) #zespolone
print(dodaj('burczy','mucha')) #napisy
print(dodaj([1,2],[3,4])) #listy
9
10.34
(3+4j)
burczymucha
[1, 2, 3, 4]
Oczywiście, wszystkiego ze sobą nie doda:
In [43]:
print(dodaj('abc',1))
-------------------------------------------------------------------------TypeError
Traceback (most recent ca
ll last)
<ipython-input-43-71790bce1fea> in <module>()
----> 1 print(dodaj('abc',1))
<ipython-input-41-94965a25a78b> in dodaj(x, y)
1 def dodaj(x,y):
----> 2
a=x+y
3
return a
TypeError: Can't convert 'int' object to str implicitly
Nazwy funkcji i zmienne
Powyżej widzieliśmy już, że funkcjom zdefiniowanym w Pythonie odpowiadają obiekty określonego typu.
Obiekty te możemy przypisywać innym zmiennym:
In [44]:
fun = dodaj
print(fun)
<function dodaj at 0x7ffbd835dd08>
In [45]:
fun(1,2)
Out[45]:
3
Argumenty i parametry
Wewnątrz funkcji, argumenty jej wywołania są przypisane do zmiennych nazywanych parametrami.
Parametry są zmiennymi lokalnymi, tzn. istnieją tylko wewnątrz bloku definiowanego przez funkcję:
In [46]:
def concat(text1,text2):
txt = text1 + text2
print(txt)
In [47]:
concat("Nad naszą wsią ","przeleciał meteoryt.")
Nad naszą wsią przeleciał meteoryt.
Po zakończeniu działania funkcji wszystkie zmienne, które zostały zdefiniowane w jej ciele, zostają
zniszczone. Dlatego próba odwołania do nich generuje błąd:
In [48]:
print(txt)
-------------------------------------------------------------------------NameError
Traceback (most recent ca
ll last)
<ipython-input-48-54ac9f963c34> in <module>()
----> 1 print(txt)
NameError: name 'txt' is not defined
Przez referencję czy przez wartość?
W dokumentacji Pythona możemy przeczytać (https://docs.python.org/3/tutorial/controlflow.html#defining
functions (https://docs.python.org/3/tutorial/controlflow.html#definingfunctions)), że przekazywanie
argumentów odbywa się zawsze przez wartość, przy czym ta wartość jest zawsze referencją do obiektu, a
nie wartością tego obiektu. W praktyce oznacza to, że w przypadku niezmiennych typów danych funkcje w
Pythonie działają tak, jakby ich argumenty przekazywane były przez wartość, natomiast w przypadku typów
zmiennych na ogół jak przez referencję.
Jako pierwszy przykład rozważymy funkcję, która zwiększa wartość argumentu o 1:
In [49]:
def add_1(number):
number = number + 1
print("Wartość wewnątrz funkcji: ", number)
return number
In [50]:
number = 10
print("Wartość przed wykonaniem funkcji: ", number)
add_1(number)
print("Wartość po wykonaniu funkcji: ", number)
Wartość przed wykonaniem funkcji: 10
Wartość wewnątrz funkcji: 11
Wartość po wykonaniu funkcji: 10
Wynik ten zrozumiemy, jeżeli przypomnimy sobie, jak działa Python wiersz
number = number + 1
nie zmienia wartości obiektu, do którego odnosi się zmienna number, a tworzy nowy obiekt o wartości number + 1 i następnie przypisuje mu etykietę number. Ponieważ wewnątrz funkcji ta zmienna ma
charakter lokalny, wskazuje ona na inny obiekt, niż zmienna o tej samej nazwie zdefiniowana poza ciałem
funkcji.
Sytuacja będzie wyglądać trochę inaczej dla funkcji, której argumentem jest lista:
In [51]:
def changeme(mylist):
mylist.append([1,2,3,4])
print("Wartość wewnątrz funkcji: ", mylist)
In [52]:
lista = [10,20,30]
print ("Wartość przed wykonaniem funkcji: ", lista)
changeme(lista)
print ("Wartość po wykonaniu funkcji: ", lista)
Wartość przed wykonaniem funkcji: [10, 20, 30]
Wartość wewnątrz funkcji: [10, 20, 30, [1, 2, 3, 4]]
Wartość po wykonaniu funkcji: [10, 20, 30, [1, 2, 3, 4]]
W tym wypadku oryginał również uległ zmianie, tzn. wynik jest taki, jak w przypadku przekazywania wartości
przez referencję w przypadku tradycyjnych języków programowania.
Jednak również w przypadku list (ogólniej zmiennych typów danych) możliwe jest zdefiniowanie funkcji,
która zachowuje się tak, jakby argument przekazywany był przez wartość:
In [53]:
def changeme2(mylist):
mylist = mylist[:]
mylist.append([1,2,3,4])
print("Wartość wewnątrz funkcji: ", mylist)
In [54]:
lista = [10,20,30]
print("Wartość przed wykonaniem funkcji: ", lista)
changeme2(lista)
print("Wartość po wykonaniu funkcji: ", lista)
Wartość przed wykonaniem funkcji: [10, 20, 30]
Wartość wewnątrz funkcji: [10, 20, 30, [1, 2, 3, 4]]
Wartość po wykonaniu funkcji: [10, 20, 30]
Różnica w porównaniu z poprzednim przykładem jest taka, że teraz wewnątrz funkcji stworzyliśmy nowy
obiekt w pamięci, a następnie przypisaliśmy mu lokalną nazwę mylist. Po zakończeniu funkcji nazwa ta
przestała istnieć, i Python wrócił do nazwy globalnej mylist, która wskazuje na inny obiekt w pamięci.
Domyślne wartości argumentów
Python wspiera kilka mechanizmów pozwalających na wywoływanie funkcji ze zmienną liczbą argumentów.
Jednym z nich jest możliwość określenia wartości jednego lub większej liczby argumentów na liście
argumentów formalnych:
In [55]:
def wypisz(wiadomosc, powtorz=1):
print(wiadomosc*powtorz)
In [56]:
wypisz('Zbliża się')
Zbliża się
In [57]:
wypisz('Sesja ',3)
Sesja Sesja Sesja
Wartość domyślna ustalana jest w momencie definiowania funkcji:
In [58]:
i=5
def f(arg=i):
print(arg)
i=6
f()
5
Uwaga! Wartość domyślna jest ustalana tylko raz. Ma to pewne konsekwencje w sytuacji, w której
argumentem domyślnym jest obiekt, który może być zmieniany. Dla przykładu, następująca funkcja
zapamiętuje argumenty jej wywołań:
In [59]:
def f(a, L=[]):
L.append(a)
return L
In [60]:
print(f(1))
print(f(2))
print(f(3))
[1]
[1, 2]
[1, 2, 3]
Jeżeli jest to działanie niepożądane, musimy zmodyfikować nieco definicję funkcji:
In [61]:
def f(a,L=None):
if L is None:
L = []
L.append(a)
return L
In [62]:
print(f(1))
print(f(2))
print(f(3))
[1]
[2]
[3]
Argumenty kluczowe
Inna możliwość to użycie argumentów kluczowych w formie klucz=wartość:
In [63]:
def papuga(napiecie, stan='racja', akcja='voom', typ='Norwegian Blue'):
print("-- Ta papuga nie zrobiłaby", akcja, ',')
print("jeśli przyłożysz do niej", napiecie, "woltów.")
print("-- Śliczne upierzenie, ten", typ)
print("-- Tak,", stan, "!")
Taką funkcję możemy wywołać na kilka sposobów:
In [64]:
papuga(1000)
-- Ta papuga nie zrobiłaby voom ,
jeśli przyłożysz do niej 1000 woltów.
-- Śliczne upierzenie, ten Norwegian Blue
-- Tak, racja !
In [65]:
papuga(akcja = 'VOOOOOM', napiecie = 1000000)
-- Ta papuga nie zrobiłaby VOOOOOM ,
jeśli przyłożysz do niej 1000000 woltów.
-- Śliczne upierzenie, ten Norwegian Blue
-- Tak, racja !
In [66]:
papuga('tysiąc', stan = 'już wącha kwiatki od spodu')
-- Ta papuga nie zrobiłaby voom ,
jeśli przyłożysz do niej tysiąc woltów.
-- Śliczne upierzenie, ten Norwegian Blue
-- Tak, już wącha kwiatki od spodu !
Jednak nie wszystkie wywołania będą prawidłowe:
In [67]:
papuga() #brakuje wymaganego argumentu niekluczowego (napiecie)
-------------------------------------------------------------------------TypeError
Traceback (most recent ca
ll last)
<ipython-input-67-75833ee02938> in <module>()
----> 1 papuga() #brakuje wymaganego argumentu niekluczowego (napie
cie)
TypeError: papuga() missing 1 required positional argument: 'napiec
ie'
In [68]:
papuga(napiecie=5.0,'trup') #niekluczowy argument za kluczowym
File "<ipython-input-68-5bc21b90ed2e>", line 1
papuga(napiecie=5.0,'trup') #niekluczowy argument za kluczowym
^
SyntaxError: non-keyword arg after keyword arg
In [69]:
papuga(110,napiecie=220) #zduplikowana wartość parametru
-------------------------------------------------------------------------TypeError
Traceback (most recent ca
ll last)
<ipython-input-69-b77f6d85e7e8> in <module>()
----> 1 papuga(110,napiecie=220) #zduplikowana wartość parametru
TypeError: papuga() got multiple values for argument 'napiecie'
In [70]:
papuga(aktor="John Cleese") #nieznany klucz
-------------------------------------------------------------------------TypeError
Traceback (most recent ca
ll last)
<ipython-input-70-80b90f873433> in <module>()
----> 1 papuga(aktor="John Cleese") #nieznany klucz
TypeError: papuga() got an unexpected keyword argument 'aktor'
W ogólności lista parametrów wywołania musi mieć jakiś argument pozycyjny, po którym następuje
jakikolwiek argument kluczowy, gdzie klucze wybrane są z listy parametrów formalnych (w definicji funkcji).
Nie jest ważne, czy parametr formalny ma wartość domyślną, czy też nie. Żaden z argumentów nie może
otrzymać wartości więcej niż jeden raz nazwy parametrów formalnych odpowiadające argumentom
pozycyjnym w wywołaniu nie mogą być w nim użyte jako kluczowe:
In [71]:
def fun(a):
pass
In [72]:
fun(0,a=0)
-------------------------------------------------------------------------TypeError
Traceback (most recent ca
ll last)
<ipython-input-72-8b4daa4ef384> in <module>()
----> 1 fun(0,a=0)
TypeError: fun() got multiple values for argument 'a'
Lista dowolnych argumentów
Gdy na liście parametrów pojawi się argument *nazwa, w momencie wywołania stanie się on krotką
zawierającą wszystkie argumenty pozycyjne (niekluczowe) wymienione w wywołaniu funkcji za parametrami
formalnymi:
In [73]:
def calkowite_wydatki(*wydatki):
return sum(wydatki)
In [74]:
calkowite_wydatki(1,2,3,4)
Out[74]:
10
In [75]:
calkowite_wydatki(1,2,3,4,5,6,7,8,9,10)
Out[75]:
55
Jeszcze jeden przykład:
In [76]:
def sklep_z_serami(rodzaj, *argumenty):
print("-- Czy macie", rodzaj, '?')
print("-- Przykro mi,", rodzaj, "właśnie się skończył.")
for arg in argumenty: print(arg)
In [77]:
sklep_z_serami('Limburger', "Jest bardzo dojrzały, proszę pana.",
"Jest naprawdę bardzo, BARDZO dojrzały, proszę pana.")
-- Czy macie Limburger ?
-- Przykro mi, Limburger właśnie się skończył.
Jest bardzo dojrzały, proszę pana.
Jest naprawdę bardzo, BARDZO dojrzały, proszę pana.
Podobnie, jeśli na liście parametrów formalnych pojawi się **nazwa, to przy wywołaniu funkcji przypisywany
jest mu słownik zawierający wszystkie klucze, które nie odpowiadają nazwom parametrów formalnych:
In [78]:
def slownik(**wpisy):
return wpisy
In [79]:
tel = slownik(Yoda=1111, Chewie=2222, Darth=6666)
print(tel)
{'Darth': 6666, 'Chewie': 2222, 'Yoda': 1111}
I jeszcze jeden przykład:
In [80]:
def sklep_z_serami(rodzaj, *argumenty, **klucze):
print("-- Czy macie", rodzaj, '?')
print("-- Przykro mi,", rodzaj, "właśnie się skończył.")
for arg in argumenty: print(arg)
print('-'*40)
for kl in klucze.keys(): print(kl, ':', klucze[kl])
In [81]:
sklep_z_serami('Limburger', "Jest bardzo dojrzały, proszę pana.",
"Jest naprawdę bardzo, BARDZO dojrzały, proszę pana.",
klient='John Cleese',
wlasciciel='Michael Palin',
skecz='Skecz ze sklepem z serami')
-- Czy macie Limburger ?
-- Przykro mi, Limburger właśnie się skończył.
Jest bardzo dojrzały, proszę pana.
Jest naprawdę bardzo, BARDZO dojrzały, proszę pana.
---------------------------------------skecz : Skecz ze sklepem z serami
wlasciciel : Michael Palin
klient : John Cleese
Formy lambda
Formy lambda to zapożyczenie z języków funkcjonalnych. To małe, anonimowe (nienazwane) funkcje, które
mogą zostać użyte we wszystkich miejscach, w których wymagane są obiekty funkcji. Składniowo
ograniczone są do pojedynczego wyrażenia:
In [82]:
def dodawacz(n):
return lambda x:x+n
In [83]:
f = dodawacz(42)
In [84]:
print(f(0))
print(f(10))
42
52
In [85]:
g = dodawacz(3.14)
In [86]:
print(g(1))
print(g(10))
4.140000000000001
13.14
Inny przykład:
In [87]:
pary = [(1,'jeden'),(2,'dwa'),(3,'trzy'),(4,'cztery')]
In [88]:
pary.sort(key=lambda para: para[1])
In [89]:
print(pary)
[(4, 'cztery'), (2, 'dwa'), (1, 'jeden'), (3, 'trzy')]
Jeszcze inny przykład:
In [90]:
def sq(x):
return x**2
liczby = [1,2,3,4,5,6,7,8]
list(map(sq,liczby))
Out[90]:
[1, 4, 9, 16, 25, 36, 49, 64]
In [91]:
liczby = [1,2,3,4,5,6,7,8]
list(map(lambda x: x**2,liczby))
Out[91]:
[1, 4, 9, 16, 25, 36, 49, 64]