Python Language
Funkcje
Szukaj…
Wprowadzenie
Funkcje w Pythonie zapewniają zorganizowany, wielokrotnego użytku i modułowy kod do wykonywania zestawu określonych akcji. Funkcje upraszczają proces kodowania, zapobiegają redundantnej logice i ułatwiają śledzenie kodu. W tym temacie opisano deklarację i wykorzystanie funkcji w języku Python.
Python ma wiele wbudowanych funkcji, takich jak print()
, input()
, len()
. Oprócz wbudowanych możesz także tworzyć własne funkcje do wykonywania bardziej szczegółowych zadań - są to tak zwane funkcje zdefiniowane przez użytkownika .
Składnia
- def function_name (arg1, ..., * args argN, kw1, kw2 = domyślne, ..., ** kwargs): Sprawozdania
- lambda arg1, ... argN, * args, kw1, kw2 = default, ..., ** kwargs : wyrażenie
Parametry
Parametr | Detale |
---|---|
arg1 , ..., argN | Regularne argumenty |
* argumenty | Nienazwane argumenty pozycyjne |
kw1 , ..., kwN | Argumenty zawierające tylko słowa kluczowe |
** kwargs | Reszta argumentów słów kluczowych |
Uwagi
5 podstawowych rzeczy, które możesz zrobić za pomocą funkcji:
Przypisz funkcje do zmiennych
def f(): print(20) y = f y() # Output: 20
Zdefiniuj funkcje w ramach innych funkcji ( Funkcje zagnieżdżone )
def f(a, b, y): def inner_add(a, b): # inner_add is hidden from outer code return a + b return inner_add(a, b)**y
Funkcje mogą zwracać inne funkcje
def f(y): def nth_power(x): return x ** y return nth_power # returns a function squareOf = f(2) # function that returns the square of a number cubeOf = f(3) # function that returns the cube of a number squareOf(3) # Output: 9 cubeOf(2) # Output: 8
Funkcje mogą być przekazywane jako parametry do innych funkcji
def a(x, y): print(x, y) def b(fun, str): # b has two arguments: a function and a string fun('Hello', str) b(a, 'Sophia') # Output: Hello Sophia
Funkcje wewnętrzne mają dostęp do obejmującego zakresu ( zamknięcie )
def outer_fun(name): def inner_fun(): # the variable name is available to the inner function return "Hello "+ name + "!" return inner_fun greet = outer_fun("Sophia") print(greet()) # Output: Hello Sophia!
Dodatkowe zasoby
- Więcej informacji o funkcjach i dekoratorach: https://www.thecodeship.com/patterns/guide-to-python-function-decorators/
Definiowanie i wywoływanie prostych funkcji
Użycie instrukcji def
jest najczęstszym sposobem definiowania funkcji w pythonie. Ta instrukcja jest tak zwaną instrukcją złożoną z pojedynczą klauzulą o następującej składni:
def function_name(parameters):
statement(s)
function_name
jest znana jako identyfikator funkcji. Ponieważ definicja funkcji jest instrukcją wykonywalną, jej wykonanie wiąże nazwę funkcji z obiektem funkcji, który można później wywołać za pomocą identyfikatora.
parameters
to opcjonalna lista identyfikatorów, które zostają powiązane z wartościami podanymi jako argumenty podczas wywoływania funkcji. Funkcja może mieć dowolną liczbę argumentów oddzielonych przecinkami.
statement(s)
- zwana także ciałem funkcji - jest niepustą sekwencją instrukcji wykonywanych przy każdym wywołaniu funkcji. Oznacza to, że ciało funkcji nie może być puste, podobnie jak każdy wcięty blok .
Oto przykład prostej definicji funkcji, której celem jest wypisywanie Hello
każdym razem, gdy się nazywa:
def greet():
print("Hello")
Teraz wywołajmy zdefiniowaną funkcję greet()
:
greet()
# Out: Hello
To kolejny przykład definicji funkcji, która przyjmuje jeden argument i wyświetla przekazaną wartość przy każdym wywołaniu funkcji:
def greet_two(greeting):
print(greeting)
Następnie należy greet_two()
funkcję greet_two()
z argumentem:
greet_two("Howdy")
# Out: Howdy
Możesz także nadać domyślną wartość argumentowi tej funkcji:
def greet_two(greeting="Howdy"):
print(greeting)
Teraz możesz wywołać funkcję bez podawania wartości:
greet_two()
# Out: Howdy
Zauważysz, że w przeciwieństwie do wielu innych języków, nie musisz jawnie deklarować typu zwracanego przez funkcję. Funkcje języka Python mogą zwracać wartości dowolnego typu za pomocą słowa kluczowego return
. Jedna funkcja może zwrócić dowolną liczbę różnych typów!
def many_types(x):
if x < 0:
return "Hello!"
else:
return 0
print(many_types(1))
print(many_types(-1))
# Output:
0
Hello!
Dopóki jest to obsługiwane przez program wywołujący, jest to całkowicie poprawny kod Pythona.
Funkcja, która osiągnie koniec wykonywania bez instrukcji return, zawsze zwróci None
:
def do_nothing():
pass
print(do_nothing())
# Out: None
Jak wspomniano wcześniej, definicja funkcji musi mieć treść funkcji, niepustą sekwencję instrukcji. Dlatego instrukcja pass
jest używana jako treść funkcji, która jest operacją zerową - kiedy jest wykonywana, nic się nie dzieje. Robi to, co znaczy, przeskakuje. Jest przydatny jako symbol zastępczy, gdy instrukcja jest wymagana składniowo, ale nie trzeba wykonywać kodu.
Zwracanie wartości z funkcji
Funkcje mogą return
wartość, której można użyć bezpośrednio:
def give_me_five():
return 5
print(give_me_five()) # Print the returned value
# Out: 5
lub zapisz wartość do późniejszego użycia:
num = give_me_five()
print(num) # Print the saved returned value
# Out: 5
lub użyj wartości do dowolnych operacji:
print(give_me_five() + 10)
# Out: 15
Jeśli w funkcji wystąpi return
funkcja zostanie natychmiast zakończona, a kolejne operacje nie będą oceniane:
def give_me_another_five():
return 5
print('This statement will not be printed. Ever.')
print(give_me_another_five())
# Out: 5
Możesz także return
wiele wartości (w postaci krotki):
def give_me_two_fives():
return 5, 5 # Returns two 5
first, second = give_me_two_fives()
print(first)
# Out: 5
print(second)
# Out: 5
Funkcja bez instrukcji return
domyślnie zwraca None
. Podobnie funkcja z instrukcją return
, ale bez zwracanej wartości lub zmiennej zwraca None
.
Definiowanie funkcji za pomocą argumentów
Argumenty są zdefiniowane w nawiasach po nazwie funkcji:
def divide(dividend, divisor): # The names of the function and its arguments
# The arguments are available by name in the body of the function
print(dividend / divisor)
Nazwa funkcji i jej lista argumentów nazywane są podpisem funkcji. Każdy nazwany argument jest faktycznie lokalną zmienną funkcji.
Podczas wywoływania funkcji podaj wartości argumentów, wymieniając je w kolejności
divide(10, 2)
# output: 5
lub określ je w dowolnej kolejności, używając nazw z definicji funkcji:
divide(divisor=2, dividend=10)
# output: 5
Definiowanie funkcji z opcjonalnymi argumentami
Opcjonalne argumenty można zdefiniować, przypisując (używając =
) wartość domyślną do nazwy argumentu:
def make(action='nothing'):
return action
Wywołanie tej funkcji jest możliwe na 3 różne sposoby:
make("fun")
# Out: fun
make(action="sleep")
# Out: sleep
# The argument is optional so the function will use the default value if the argument is
# not passed in.
make()
# Out: nothing
Ostrzeżenie
Typy zmienne (
list
,dict
,set
itp.) Należy traktować ostrożnie, jeśli podano je jako domyślny atrybut. Każda mutacja domyślnego argumentu zmieni go na stałe. Zobacz Definiowanie funkcji z opcjonalnymi argumentami zmiennymi .
Definiowanie funkcji z wieloma argumentami
Można podać funkcji tyle argumentów, ile się chce, jedynymi ustalonymi regułami jest to, że nazwa każdego argumentu musi być unikalna, a argumenty opcjonalne muszą znajdować się po argumentach nie opcjonalnych:
def func(value1, value2, optionalvalue=10):
return '{0} {1} {2}'.format(value1, value2, optionalvalue1)
Podczas wywoływania funkcji możesz podać każde słowo kluczowe bez nazwy, ale wtedy kolejność ma znaczenie:
print(func(1, 'a', 100))
# Out: 1 a 100
print(func('abc', 14))
# abc 14 10
Lub połącz podawanie argumentów z nazwą i bez. Zatem ci z imieniem muszą iść za tymi bez, ale kolejność tych z imieniem nie ma znaczenia:
print(func('This', optionalvalue='StackOverflow Documentation', value2='is'))
# Out: This is StackOverflow Documentation
Zdefiniowanie funkcji z dowolną liczbą argumentów
Dowolna liczba argumentów pozycyjnych:
Zdefiniowanie funkcji zdolnej do przyjęcia dowolnej liczby argumentów można wykonać, poprzedzając jeden z argumentów znakiem *
def func(*args):
# args will be a tuple containing all values that are passed in
for i in args:
print(i)
func(1, 2, 3) # Calling it with 3 arguments
# Out: 1
# 2
# 3
list_of_arg_values = [1, 2, 3]
func(*list_of_arg_values) # Calling it with list of values, * expands the list
# Out: 1
# 2
# 3
func() # Calling it without arguments
# No Output
Nie możesz podać wartości domyślnej dla args
, na przykład func(*args=[1, 2, 3])
wywoła błąd składniowy (nawet się nie skompiluje).
Nie można dostarczyć te nazwy po wywołaniu funkcji, na przykład func(*args=[1, 2, 3])
podniesie TypeError
.
Ale jeśli masz już swoje argumenty w tablicy (lub dowolny inny Iterable
), można powołać swoją funkcję tak: func(*my_stuff)
.
Do tych argumentów ( *args
) można uzyskać dostęp za pomocą indeksu, na przykład args[0]
zwróci pierwszy argument
Dowolna liczba argumentów słów kluczowych
Możesz wziąć dowolną liczbę argumentów o nazwie, definiując argument w definicji z dwoma *
przed nim:
def func(**kwargs):
# kwargs will be a dictionary containing the names as keys and the values as values
for name, value in kwargs.items():
print(name, value)
func(value1=1, value2=2, value3=3) # Calling it with 3 arguments
# Out: value1 1
# value2 2
# value3 3
func() # Calling it without arguments
# No Out put
my_dict = {'foo': 1, 'bar': 2}
func(**my_dict) # Calling it with a dictionary
# Out: foo 1
# bar 2
Nie można zapewnić bez ich nazw, na przykład func(1, 2, 3)
podniesie TypeError
.
kwargs
jest prostym rodzimym słownikiem Pythona. Na przykład args['value1']
poda wartość argumentu value1
. Pamiętaj, aby wcześniej sprawdzić, czy istnieje taki argument lub w przeciwnym razie zostanie KeyError
.
Ostrzeżenie
Można je łączyć z innymi opcjonalnymi i wymaganymi argumentami, ale kolejność w definicji ma znaczenie.
Argumenty pozycyjne / kluczowe są na pierwszym miejscu. (Wymagane argumenty).
Potem przychodzą arbitralne *arg
argumenty *arg
. (Opcjonalny).
Następnie pojawiają się argumenty zawierające tylko słowa kluczowe . (Wymagany).
Wreszcie **kwargs
się dowolne słowo kluczowe **kwargs
. (Opcjonalny).
# |-positional-|-optional-|---keyword-only--|-optional-|
def func(arg1, arg2=10 , *args, kwarg1, kwarg2=2, **kwargs):
pass
-
arg1
musi zostać podany, w przeciwnym razie wywoływany jest błądTypeError
. Można go podać jako argument pozycyjny (func(10)
) lub jako argument słowa kluczowego (func(arg1=10)
). - należy również podać
kwarg1
, ale można go podać tylko jako słowo-argument:func(kwarg1=10)
. -
arg2
ikwarg2
są opcjonalne. Jeśli wartość ma zostać zmieniona, obowiązują takie same zasady jak dlaarg1
(pozycyjne lub słowo kluczowe) ikwarg1
(tylko słowo kluczowe). -
*args
wyłapuje dodatkowe parametry pozycyjne. Zauważ jednak, żearg1
iarg2
muszą być dostarczone jako argumenty pozycyjne, aby przekazać argumenty do*args
:func(1, 1, 1, 1)
. -
**kwargs
przechwytuje wszystkie dodatkowe parametry słów kluczowych. W tym przypadku każdy parametr, który nie jestarg1
,arg2
,kwarg1
lubkwarg2
. Na przykład:func(kwarg3=10)
. - W Pythonie 3 możesz użyć samego
*
, aby wskazać, że wszystkie kolejne argumenty muszą być określone jako słowa kluczowe. Na przykład funkcjamath.isclose
w Pythonie 3.5 i nowszych jest zdefiniowana za pomocądef math.isclose (a, b, *, rel_tol=1e-09, abs_tol=0.0)
, co oznacza, że dwa pierwsze argumenty mogą być podane pozycjonalnie, ale opcjonalnie trzeci i czwarty parametr można podać tylko jako argumenty słów kluczowych.
Python 2.x nie obsługuje parametrów zawierających tylko słowa kluczowe. To zachowanie można emulować za pomocą programu kwargs
:
def func(arg1, arg2=10, **kwargs):
try:
kwarg1 = kwargs.pop("kwarg1")
except KeyError:
raise TypeError("missing required keyword-only argument: 'kwarg1'")
kwarg2 = kwargs.pop("kwarg2", 2)
# function body ...
Uwaga na temat nazewnictwa
Konwencja nazywania opcjonalnych argumentów pozycyjnych args
i opcjonalnych argumentów słów kluczowych kwargs
to tylko konwencja, w której możesz używać dowolnych nazw, które lubisz, ale warto postępować zgodnie z konwencją, aby inni wiedzieli, co robisz, a nawet sam później, więc proszę.
Uwaga na temat wyjątkowości
Dowolną funkcję można zdefiniować za pomocą żadnego lub jednego *args
i żadnego lub jednego **kwargs
ale nie więcej niż jednym z nich. Również *args
musi być ostatnim argumentem pozycyjnym, a **kwargs
musi być ostatnim parametrem. Próba użycia więcej niż jednego z nich spowoduje wyjątek błędu składni.
Uwaga na temat funkcji zagnieżdżania z opcjonalnymi argumentami
Możliwe jest zagnieżdżanie takich funkcji, a zwykłą konwencją jest usuwanie elementów, które kod już obsługiwał, ale jeśli przekazujesz parametry, musisz przekazać opcjonalne argumenty pozycyjne z prefiksem *
i opcjonalne args słowa kluczowego z prefiksem **
, w przeciwnym razie argumenty zostaną przekazane jako lista lub krotka, a kwargs jako pojedynczy słownik. na przykład:
def fn(**kwargs):
print(kwargs)
f1(**kwargs)
def f1(**kwargs):
print(len(kwargs))
fn(a=1, b=2)
# Out:
# {'a': 1, 'b': 2}
# 2
Definiowanie funkcji z opcjonalnymi argumentami zmiennymi
Występuje problem podczas używania opcjonalnych argumentów ze zmiennym typem domyślnym (opisanym w Definiowaniu funkcji z opcjonalnymi argumentami ), który może potencjalnie prowadzić do nieoczekiwanego zachowania.
Wyjaśnienie
Ten problem powstaje, ponieważ domyślne argumenty funkcji są inicjowane jeden raz , w momencie zdefiniowania funkcji, a nie (jak wiele innych języków), gdy funkcja jest wywoływana . Domyślne wartości są przechowywane w zmiennej __defaults__
obiektu funkcji.
def f(a, b=42, c=[]):
pass
print(f.__defaults__)
# Out: (42, [])
W przypadku typów niezmiennych (patrz Przekazywanie i modyfikowanie argumentów ) nie stanowi to problemu, ponieważ nie ma możliwości zmutowania zmiennej; można go tylko przypisać ponownie, pozostawiając pierwotną wartość bez zmian. W związku z tym kolejne gwarantują, że będą miały tę samą wartość domyślną. Jednak w przypadku typu zmiennego oryginalna wartość może mutować, wywołując różne funkcje składowe. Dlatego nie ma gwarancji, że kolejne wywołania funkcji będą miały początkową wartość domyślną.
def append(elem, to=[]):
to.append(elem) # This call to append() mutates the default variable "to"
return to
append(1)
# Out: [1]
append(2) # Appends it to the internally stored list
# Out: [1, 2]
append(3, []) # Using a new created list gives the expected result
# Out: [3]
# Calling it again without argument will append to the internally stored list again
append(4)
# Out: [1, 2, 4]
Uwaga: Niektóre IDE, takie jak PyCharm, będą wyświetlać ostrzeżenie, gdy typ zmienny zostanie określony jako domyślny atrybut.
Rozwiązanie
Jeśli chcesz się upewnić, że domyślnym argumentem jest zawsze argument określony w definicji funkcji, rozwiązaniem jest zawsze użycie niezmiennego typu jako domyślnego argumentu.
Powszechnym idiomem służącym do osiągnięcia tego, gdy domyślnym typem jest zmienny typ, jest użycie None
(niezmienny) jako domyślny argument, a następnie przypisanie rzeczywistej wartości domyślnej do zmiennej argumentu, jeśli jest ona równa None
.
def append(elem, to=None):
if to is None:
to = []
to.append(elem)
return to
Funkcje Lambda (wbudowane / anonimowe)
lambda
kluczowe lambda
tworzy funkcję wbudowaną, która zawiera pojedyncze wyrażenie. Wartością tego wyrażenia jest to, co funkcja zwraca po wywołaniu.
Rozważ funkcję:
def greeting():
return "Hello"
który, nazywany jako:
print(greeting())
drukuje:
Hello
Można to zapisać jako funkcję lambda w następujący sposób:
greet_me = lambda: "Hello"
Patrz uwaga na dole tego rozdziału dotycząca przypisywania lambda do zmiennych. Ogólnie nie rób tego.
To tworzy funkcję wbudowaną o nazwie greet_me
która zwraca Hello
. Zauważ, że nie piszesz return
podczas tworzenia funkcji z lambda. Wartość po :
jest automatycznie zwracana.
Po przypisaniu do zmiennej może być używana tak jak zwykła funkcja:
print(greet_me())
drukuje:
Hello
lambda
może przyjmować argumenty:
strip_and_upper_case = lambda s: s.strip().upper()
strip_and_upper_case(" Hello ")
zwraca ciąg:
HELLO
Mogą również przyjmować dowolną liczbę argumentów / argumentów słów kluczowych, tak jak normalne funkcje.
greeting = lambda x, *args, **kwargs: print(x, args, kwargs)
greeting('hello', 'world', world='world')
drukuje:
hello ('world',) {'world': 'world'}
lambda
są powszechnie używane do krótkich funkcji, które są wygodne do zdefiniowania w punkcie, w którym są wywoływane (zwykle z sorted
, filter
i map
).
Na przykład ten wiersz sortuje listę ciągów, ignorując ich wielkość liter i ignorując białe znaki na początku i na końcu:
sorted( [" foo ", " bAR", "BaZ "], key=lambda s: s.strip().upper())
# Out:
# [' bAR', 'BaZ ', ' foo ']
Sortuj listę ignorując białe spacje:
sorted( [" foo ", " bAR", "BaZ "], key=lambda s: s.strip())
# Out:
# ['BaZ ', ' bAR', ' foo ']
Przykłady z map
:
sorted( map( lambda s: s.strip().upper(), [" foo ", " bAR", "BaZ "]))
# Out:
# ['BAR', 'BAZ', 'FOO']
sorted( map( lambda s: s.strip(), [" foo ", " bAR", "BaZ "]))
# Out:
# ['BaZ', 'bAR', 'foo']
Przykłady z listami numerycznymi:
my_list = [3, -4, -2, 5, 1, 7]
sorted( my_list, key=lambda x: abs(x))
# Out:
# [1, -2, 3, -4, 5, 7]
list( filter( lambda x: x>0, my_list))
# Out:
# [3, 5, 1, 7]
list( map( lambda x: abs(x), my_list))
# Out:
[3, 4, 2, 5, 1, 7]
Można wywoływać inne funkcje (z / bez argumentów) z wnętrza funkcji lambda.
def foo(msg):
print(msg)
greet = lambda x = "hello world": foo(x)
greet()
drukuje:
hello world
Jest to przydatne, ponieważ lambda
może zawierać tylko jedno wyrażenie, a za pomocą funkcji pomocniczej można uruchamiać wiele instrukcji.
UWAGA
Pamiętaj, że PEP-8 (oficjalny przewodnik po stylu Python) nie zaleca przypisywania lambd do zmiennych (tak jak to zrobiliśmy w dwóch pierwszych przykładach):
Zawsze używaj instrukcji def zamiast instrukcji przypisania, która wiąże wyrażenie lambda bezpośrednio z identyfikatorem.
Tak:
def f(x): return 2*x
Nie:
f = lambda x: 2*x
Pierwsza forma oznacza, że nazwą wynikowego obiektu funkcji jest konkretnie
f
zamiast ogólnego<lambda>
. Jest to bardziej przydatne w przypadku śledzenia wstecznego i reprezentacji ciągów w ogóle. Zastosowanie instrukcji przypisania eliminuje jedyną korzyść, jaką wyrażenie lambda może zaoferować w stosunku do jawnej instrukcjidef
(tzn. Że można ją osadzić w większym wyrażeniu).
Przekazywanie argumentów i zmienność
Po pierwsze, trochę terminologii:
- argument ( aktualny parametr): rzeczywista zmienna przekazywana do funkcji;
- parametr (parametr formalny ): zmienna odbiorcza używana w funkcji.
W Pythonie argumenty są przekazywane przez przypisanie (w przeciwieństwie do innych języków, w których argumenty mogą być przekazywane przez wartość / referencję / wskaźnik).
Mutowanie parametru spowoduje mutację argumentu (jeśli typ argumentu jest zmienny).
def foo(x): # here x is the parameter x[0] = 9 # This mutates the list labelled by both x and y print(x) y = [4, 5, 6] foo(y) # call foo with y as argument # Out: [9, 5, 6] # list labelled by x has been mutated print(y) # Out: [9, 5, 6] # list labelled by y has been mutated too
Ponowne przypisanie parametru nie spowoduje ponownego przypisania argumentu.
def foo(x): # here x is the parameter, when we call foo(y) we assign y to x x[0] = 9 # This mutates the list labelled by both x and y x = [1, 2, 3] # x is now labeling a different list (y is unaffected) x[2] = 8 # This mutates x's list, not y's list y = [4, 5, 6] # y is the argument, x is the parameter foo(y) # Pretend that we wrote "x = y", then go to line 1 y # Out: [9, 5, 6]
W Pythonie, tak naprawdę nie należy przypisać wartości do zmiennych, zamiast tego bind (tj assign, dołącz) zmienne (uważane za nazwami) do obiektów.
- Niezmienne: liczby całkowite, łańcuchy, krotki i tak dalej. Wszystkie operacje wykonują kopie.
- Zmienne: listy, słowniki, zestawy i tak dalej. Operacje mogą mutować, ale nie muszą.
x = [3, 1, 9]
y = x
x.append(5) # Mutates the list labelled by x and y, both x and y are bound to [3, 1, 9]
x.sort() # Mutates the list labelled by x and y (in-place sorting)
x = x + [4] # Does not mutate the list (makes a copy for x only, not y)
z = x # z is x ([1, 3, 9, 4])
x += [6] # Mutates the list labelled by both x and z (uses the extend function).
x = sorted(x) # Does not mutate the list (makes a copy for x only).
x
# Out: [1, 3, 4, 5, 6, 9]
y
# Out: [1, 3, 5, 9]
z
# Out: [1, 3, 5, 9, 4, 6]
Zamknięcie
Zamknięcia w Pythonie są tworzone przez wywołania funkcji. W tym przypadku wywołanie makeInc
tworzy powiązanie dla x
którego odwołuje się funkcja inc
. Każde wywołanie makeInc
tworzy nowe wystąpienie tej funkcji, ale każde wystąpienie ma link do innego wiązania x
.
def makeInc(x):
def inc(y):
# x is "attached" in the definition of inc
return y + x
return inc
incOne = makeInc(1)
incFive = makeInc(5)
incOne(5) # returns 6
incFive(5) # returns 10
Zauważ, że podczas gdy w zwykłym zamknięciu zamknięta funkcja w pełni dziedziczy wszystkie zmienne z otaczającego ją środowiska, w tej konstrukcji zamknięta funkcja ma dostęp tylko do odczytu do odziedziczonych zmiennych, ale nie może im przypisywać przypisań
def makeInc(x):
def inc(y):
# incrementing x is not allowed
x += y
return x
return inc
incOne = makeInc(1)
incOne(5) # UnboundLocalError: local variable 'x' referenced before assignment
Python 3 oferuje instrukcję nonlocal
( Zmienne nonlocal
) umożliwiającą pełne zamknięcie funkcji zagnieżdżonych.
def makeInc(x):
def inc(y):
nonlocal x
# now assigning a value to x is allowed
x += y
return x
return inc
incOne = makeInc(1)
incOne(5) # returns 6
Funkcje rekurencyjne
Funkcja rekurencyjna to funkcja, która wywołuje się w swojej definicji. Na przykład funkcja matematyczna, silnia, zdefiniowana przez factorial(n) = n*(n-1)*(n-2)*...*3*2*1
. można zaprogramować jako
def factorial(n):
#n here should be an integer
if n == 0:
return 1
else:
return n*factorial(n-1)
dane wyjściowe są następujące:
factorial(0)
#out 1
factorial(1)
#out 1
factorial(2)
#out 2
factorial(3)
#out 6
zgodnie z oczekiwaniami. Zauważ, że ta funkcja jest rekurencyjna, ponieważ drugi return factorial(n-1)
, w którym funkcja wywołuje się w swojej definicji.
Niektóre funkcje rekurencyjne można zaimplementować za pomocą lambda , funkcja silnia za pomocą lambda byłaby mniej więcej taka:
factorial = lambda n: 1 if n == 0 else n*factorial(n-1)
Funkcja generuje to samo co powyżej.
Limit rekurencji
Istnieje limit głębokości możliwej rekurencji, który zależy od implementacji Pythona. Po osiągnięciu limitu zgłaszany jest wyjątek RuntimeError:
def cursing(depth):
try:
cursing(depth + 1) # actually, re-cursing
except RuntimeError as RE:
print('I recursed {} times!'.format(depth))
cursing(0)
# Out: I recursed 1083 times!
Można zmienić limit głębokości rekurencji za pomocą sys.setrecursionlimit(limit)
i sprawdzić ten limit za pomocą sys.getrecursionlimit()
.
sys.setrecursionlimit(2000)
cursing(0)
# Out: I recursed 1997 times!
Od wersji Python 3.5 wyjątkiem jest RecursionError
, który pochodzi z RuntimeError
.
Funkcje zagnieżdżone
Funkcje w pythonie są obiektami pierwszej klasy. Można je zdefiniować w dowolnym zakresie
def fibonacci(n):
def step(a,b):
return b, a+b
a, b = 0, 1
for i in range(n):
a, b = step(a, b)
return a
Funkcje przechwytują otaczający ich zakres, który można przekazywać jak każdy inny obiekt
def make_adder(n):
def adder(x):
return n + x
return adder
add5 = make_adder(5)
add6 = make_adder(6)
add5(10)
#Out: 15
add6(10)
#Out: 16
def repeatedly_apply(func, n, x):
for i in range(n):
x = func(x)
return x
repeatedly_apply(add5, 5, 1)
#Out: 26
Rozpakowywanie iterowalne i słownikowe
Funkcje pozwalają określić następujące typy parametrów: pozycyjny, nazwany, zmienny pozycyjny, słowa kluczowe args (kwargs). Oto jasne i zwięzłe użycie każdego rodzaju.
def unpacking(a, b, c=45, d=60, *args, **kwargs):
print(a, b, c, d, args, kwargs)
>>> unpacking(1, 2)
1 2 45 60 () {}
>>> unpacking(1, 2, 3, 4)
1 2 3 4 () {}
>>> unpacking(1, 2, c=3, d=4)
1 2 3 4 () {}
>>> unpacking(1, 2, d=4, c=3)
1 2 3 4 () {}
>>> pair = (3,)
>>> unpacking(1, 2, *pair, d=4)
1 2 3 4 () {}
>>> unpacking(1, 2, d=4, *pair)
1 2 3 4 () {}
>>> unpacking(1, 2, *pair, c=3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unpacking() got multiple values for argument 'c'
>>> unpacking(1, 2, c=3, *pair)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unpacking() got multiple values for argument 'c'
>>> args_list = [3]
>>> unpacking(1, 2, *args_list, d=4)
1 2 3 4 () {}
>>> unpacking(1, 2, d=4, *args_list)
1 2 3 4 () {}
>>> unpacking(1, 2, c=3, *args_list)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unpacking() got multiple values for argument 'c'
>>> unpacking(1, 2, *args_list, c=3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unpacking() got multiple values for argument 'c'
>>> pair = (3, 4)
>>> unpacking(1, 2, *pair)
1 2 3 4 () {}
>>> unpacking(1, 2, 3, 4, *pair)
1 2 3 4 (3, 4) {}
>>> unpacking(1, 2, d=4, *pair)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unpacking() got multiple values for argument 'd'
>>> unpacking(1, 2, *pair, d=4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unpacking() got multiple values for argument 'd'
>>> args_list = [3, 4]
>>> unpacking(1, 2, *args_list)
1 2 3 4 () {}
>>> unpacking(1, 2, 3, 4, *args_list)
1 2 3 4 (3, 4) {}
>>> unpacking(1, 2, d=4, *args_list)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unpacking() got multiple values for argument 'd'
>>> unpacking(1, 2, *args_list, d=4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unpacking() got multiple values for argument 'd'
>>> arg_dict = {'c':3, 'd':4}
>>> unpacking(1, 2, **arg_dict)
1 2 3 4 () {}
>>> arg_dict = {'d':4, 'c':3}
>>> unpacking(1, 2, **arg_dict)
1 2 3 4 () {}
>>> arg_dict = {'c':3, 'd':4, 'not_a_parameter': 75}
>>> unpacking(1, 2, **arg_dict)
1 2 3 4 () {'not_a_parameter': 75}
>>> unpacking(1, 2, *pair, **arg_dict)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unpacking() got multiple values for argument 'd'
>>> unpacking(1, 2, 3, 4, **arg_dict)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unpacking() got multiple values for argument 'd'
# Positional arguments take priority over any other form of argument passing
>>> unpacking(1, 2, **arg_dict, c=3)
1 2 3 4 () {'not_a_parameter': 75}
>>> unpacking(1, 2, 3, **arg_dict, c=3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unpacking() got multiple values for argument 'c'
Wymuszenie użycia nazwanych parametrów
Wszystkie parametry określone po pierwszej gwiazdce w podpisie funkcji są tylko słowami kluczowymi.
def f(*a, b):
pass
f(1, 2, 3)
# TypeError: f() missing 1 required keyword-only argument: 'b'
W Pythonie 3 można wstawić pojedynczą gwiazdkę do podpisu funkcji, aby pozostałe argumenty mogły być przekazywane tylko za pomocą argumentów słów kluczowych.
def f(a, b, *, c):
pass
f(1, 2, 3)
# TypeError: f() takes 2 positional arguments but 3 were given
f(1, 2, c=3)
# No error
Rekurencyjna Lambda przy użyciu przypisanej zmiennej
Jedna metoda tworzenia rekurencyjnych funkcji lambda obejmuje przypisanie funkcji do zmiennej, a następnie odwołanie się do tej zmiennej w samej funkcji. Typowym przykładem tego jest rekurencyjne obliczanie silni liczby - takie jak pokazano w poniższym kodzie:
lambda_factorial = lambda i:1 if i==0 else i*lambda_factorial(i-1)
print(lambda_factorial(4)) # 4 * 3 * 2 * 1 = 12 * 2 = 24
Opis kodu
Funkcja lambda, poprzez swoje przypisanie zmiennej, przekazuje wartość (4), którą ocenia i zwraca 1, jeśli wynosi 0, w przeciwnym razie zwraca bieżącą wartość ( i
) * kolejne obliczenie przez funkcję lambda wartości - 1 ( i-1
). Trwa to do momentu zmniejszenia wartości przekazywanej do 0 ( return 1
). Proces, który można wizualizować jako: