Funkcje i programowanie funkcyjne (funkcjonalne)

Opis funkcji, ich możliwości i programowania funkcyjnego w Pythonie. Artykuł opisuje poszczególne elementy funkcji, jak przekazywanie argumentów, wartości domyślne, dostęp danych, operator lambda i funkcje eval(), exec() i execfile()

Python pozwala łatwo tworzyć własne funkcje czerpiąc z pomysłów różnych języków programowania funkcyjnego (funkcjonalnego). Funkcję definiujemy za pomocą instrukcji def. Oto przykład:
def nasza_funkcja(argument1, argument2):
	argumenty = argument1+" - "+argument2
	return argumenty

print nasza_funkcja("Poczatek", "Koniec")
Instrukcja return zwraca podany obiekt. Jeżeli chcemy zwrócić więcej niż jedną zmienną to stosujemy np. tuplę. Aby wywołać funkcję należy napisać jej nazwę, a po niej tuplę zawierającą argumenty funkcji (funkcja może nie wymagać argumentów). Kolejność i liczba argumentów przekazanych do funkcji musi zgadzać się z definicją funkcji. Można też przekazywać parametry przypisując je konkretnym zmiennym zadeklarowanych w funkcji:
nasza_funkcja(argument1="Poczatek", argument2="Koniec")
Możemy także w definicji funkcji przypisać parametrom ("zmiennym") domyślne wartości (argumenty hasłowe), które będą użyte jeżeli przy wywołaniu funkcji nie będą podane wartości dla danych zmiennych:
def nasza_funkcja(argument1, argument2='The end'):
	argumenty = argument1+" - "+argument2
	return argumenty
print nasza_funkcja(argument1="Poczatek")
Przy przypisywaniu wartości poprzez zmienne parametrom funkcji należy pamiętać iż zostaną użyte wartości jakie miały obiekty podczas definiowana funkcji
a = 1
def test(x = a):
	return x

a = 2
print test()
Powyższy kod wyświetli "1", gdyż zmienna "a" miała wartość "1" w czasie definiowana funkcji. Używanie obiektów jako wartości domyślnych może doprowadzić do nieoczekiwanych rezultatów:
a = [1]
def test(x = a):
	return x

a.append(2)
print test()
Wynik działania funkcji "test" to:
[1, 2]
Podczas wywoływania funkcji jej parametry przekazywane są przez odwołanie. Jeżeli jakiś obiekt zmienny (jak lista lub słownik) ulegnie zmianie wewnątrz funkcji, to zmiana ta będzie widoczna również po wywołaniu funkcji. Odpowiednio jeżeli zmienimy taki obiekt po deklaracji funkcji a przed jej wywołaniem to ta zmiana zostanie uwzględniona, co tłumaczy powyższy wynik.

Jeżeli nazwa ostatniego parametru funkcji zaczyna się od gwiazdki to funkcja może przyjmować dowolną liczbę argumentów:
def test(*args):
	return args

print test('a', 'abc', 1, 2, 3)
Parametr, który w tym przykładzie nazywa się "*args" zawierać będzie tuplę ze wszystkimi przekazanymi argumentami (dodatkowymi). Natomiast jeżeli ostatni parametr funkcji zaczyna się od dwóch gwiazdek to przechwyci on wszystkie nie pasujące argumenty hasłowe - wartości przypisane parametrom o niezadeklarowanej w funkcji nazwie:
def test(test, **args):
	return args

print test(test='a', foo='abc', bar=1)
Parametr "**args" przechwyci dwa ostatnie argumenty hasłowe:
{'foo': 'abc', 'bar': 1}
Można połączyć oba specjalne parametry, z tym że ostatnim musi być parametr z dwiema gwiazdkami:
def test(*arg, **args):
	print arg
	print
	print args

test(1,2,3, 'ab2', test='a', foo='abc', bar=1)


Funkcje jak i metody mogą mieć dołączane dowolne atrybuty:
def test():
	return 'abc'

test.a = 1
test.lol = 'omg'

print test()
print test.__dict__
Atrybuty funkcji przechowywane są w słowniku dostępnym za pomocą atrybutu __dict__ funkcji.

Reguły obowiązywania zasięgu

Za każdym razem gdy dochodzi do wykonania jakiejś funkcji, tworzona jest nowa lokalna przestrzeń nazw. Nowa przestrzeń obejmuje nazwy parametrów funkcji, a także nazwy zmiennych używanych wewnątrz funkcji. Przystępując do rozwiązywania nazwy interpreter zaczyna od przeszukania lokalnej przestrzeni nazw, następnie jeżeli nie zostanie znaleziona poszukiwana nazwa interpreter przeszukuje przestrzeń globalną - moduł, w którym znajduje się funkcja. Jeżeli przeszukiwanie skończy się niepowodzeniem to przeszukiwana jest przestrzeń wbudowana. Niepowodzenie zwróci wyjątek Name-Error. Zmienne stosowane wewnątrz funkcji nie będą dostępne poza nią, chyba że przypiszemy je do globalnej przestrzeni nazw. By przypisać zmienną do przestrzeni globalnej należy użyć instrukcji global:
def test():
	global a
	a = 4
	return True

test()
print a

Operator lambda

Wyrażenie lambda służy do definiowania anonimowych funkcji w postaci wyrażenia:
lambda argument : wyrażenie
Argumenty to lista argumentów oddzielona przecinkami, a "wyrażenie" to wyrażenie wykorzystujące te argumenty. Przykłady:
a = lambda : 'a'
print a()

b = lambda x,y : x+y
print b(1,2)

Funkcje map(), zip(), reduce() i filter()

Funkcja map() wykonuje podaną funkcję dla każdego elementu listy/tupli/słownika:
liczby = [1,2,3,4,5]
def dwa(x):
	return x*2

print map(dwa, liczby)
Pierwszy argument to nazwa funkcji przyjmującej jeden argument, drugi to lista/tupla/słownik. Wynik zwracany jest w postaci listy.
Funkcja zip() tworzy za to listę tupli z podanych elementów:
liczby_n = [1,3,5]
liczby_p = [2,4,6]
print zip(liczby_n, liczby_p)
Wynik powyższego kodu to:
[(1, 2), (3, 4), (5, 6)]
Funkcja reduce() pobiera dane z sekwencji i zwraca na ich podstawie pojedynczą wartość np. sumę liczb. Funkcja ta wykonuje podaną jako pierwszy argument funkcję dla pierwszych dwóch elementów sekwencji a następnie wykonuje tą funkcję dla wyniku i trzeciego elementu - i tak do wyczerpania elementów sekwencji:
liczby = [2,2,2,2,2]
def suma(x,y):
	return x+y
print reduce(suma, liczby)
Funkcja filter filtruje elementy sekwencji przy użyciu podanej funkcji, która musi zwracać wartość Prawda lub Fałsz:
liczby = [1,2,3,4,5]
def parzyste(x):
	if x%2 == 0:
		return True
	else:
		return False

print filter(parzyste, liczby)

Funkcje eval(), exec() i execfile()

Funkcja eval() wykonuje wyrażenie zawarte w łańcuchu i zwraca wynik. Funkcja exec() wykonuje dowolny kod Pythona zawarty w łańcuchu tak jakby znajdował się w miejscu tej instrukcji, natomiast funkcja execfile() wykonuje kod podanego pliku.
print eval('2+2')
print
exec('for i in range(1,3): print i')
blog comments powered by Disqus

Kategorie

Strony