4. Więcej narzędzi kontroli przepływu¶
Besides the while
statement just introduced, Python knows the usual
control flow statements known from other languages, with some twists.
4.1. if
Statements¶
Prawdopodobnie najbardziej znanym typem instrukcji jest instrukcja if
. Na przykład:
>>> x = int(input("Please enter an integer: "))
Please enter an integer: 42
>>> if x < 0:
... x = 0
... print('Negative changed to zero')
... elif x == 0:
... print('Zero')
... elif x == 1:
... print('Single')
... else:
... print('More')
...
More
There can be zero or more elif
parts, and the else
part is
optional. The keyword «elif
» is short for «else if», and is useful
to avoid excessive indentation. An if
… elif
…
elif
… sequence is a substitute for the switch
or
case
statements found in other languages.
4.2. for
Statements¶
The for
statement in Python differs a bit from what you may be used
to in C or Pascal. Rather than always iterating over an arithmetic progression
of numbers (like in Pascal), or giving the user the ability to define both the
iteration step and halting condition (as C), Python’s for
statement
iterates over the items of any sequence (a list or a string), in the order that
they appear in the sequence. For example (no pun intended):
>>> # Measure some strings:
... words = ['cat', 'window', 'defenestrate']
>>> for w in words:
... print(w, len(w))
...
cat 3
window 6
defenestrate 12
If you need to modify the sequence you are iterating over while inside the loop (for example to duplicate selected items), it is recommended that you first make a copy. Iterating over a sequence does not implicitly make a copy. The slice notation makes this especially convenient:
>>> for w in words[:]: # Loop over a slice copy of the entire list.
... if len(w) > 6:
... words.insert(0, w)
...
>>> words
['defenestrate', 'cat', 'window', 'defenestrate']
With for w in words:
, the example would attempt to create an infinite list,
inserting defenestrate
over and over again.
4.3. Funkcja range()
¶
Jeśli potrzebujesz iterować po sekwencji liczb, przydatna jest wbudowana funkcja range()
. Generuje ciągi arytmetyczne:
>>> for i in range(5):
... print(i)
...
0
1
2
3
4
Podany punkt końcowy nigdy nie jest częścią generowanej sekwencji; range(10)
generuje 10 wartości, poprawne indeksy dla elementów sekwencji o długości 10. Możliwe jest zacząć zakres od innej liczby lub podać inne zwiększenie (nawet ujemne; czasem jest to nazywane „krokiem”):
range(5, 10)
5, 6, 7, 8, 9
range(0, 10, 3)
0, 3, 6, 9
range(-10, -100, -30)
-10, -40, -70
By przeiterować po indeksach sekwencji możesz połączyć range()
i len()
w następujący sposób:
>>> a = ['Mary', 'had', 'a', 'little', 'lamb']
>>> for i in range(len(a)):
... print(i, a[i])
...
0 Mary
1 had
2 a
3 little
4 lamb
Jednak w większości takich przypadków wygodnie jest użyć funkcji enumerate()
, patrz Techniki pętli.
Dzieje się dziwna rzecz jeśli po prostu wydrukujesz zakres:
>>> print(range(10))
range(0, 10)
Pod wieloma względami obiekt zwracany przez range()
zachowuje się, jakby był listą, ale w rzeczywistości nią nie jest. Jest obiektem, który zwraca kolejne elementy żądanej sekwencji w trakcie twojego iterowania po nim, lecz naprawdę nie tworzy listy, tak więc oszczędza miejsce w pamięci komputera.
We say such an object is iterable, that is, suitable as a target for
functions and constructs that expect something from which they can
obtain successive items until the supply is exhausted. We have seen that
the for
statement is such an iterator. The function list()
is another; it creates lists from iterables:
>>> list(range(5))
[0, 1, 2, 3, 4]
Later we will see more functions that return iterables and take iterables as argument.
4.4. break
and continue
Statements, and else
Clauses on Loops¶
Instrukcja break
, tak jak w C, wychodzi z najbardziej wewnętrznej pętli for
lub while
zawierającej tę instrukcję.
Loop statements may have an else
clause; it is executed when the loop
terminates through exhaustion of the list (with for
) or when the
condition becomes false (with while
), but not when the loop is
terminated by a break
statement. This is exemplified by the
following loop, which searches for prime numbers:
>>> for n in range(2, 10):
... for x in range(2, n):
... if n % x == 0:
... print(n, 'equals', x, '*', n//x)
... break
... else:
... # loop fell through without finding a factor
... print(n, 'is a prime number')
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3
(Tak, to poprawny kod. Przyjrzyj się: klauzula else
należy do pętli for
, nie do instrukcji if
.)
When used with a loop, the else
clause has more in common with the
else
clause of a try
statement than it does that of
if
statements: a try
statement’s else
clause runs
when no exception occurs, and a loop’s else
clause runs when no break
occurs. For more on the try
statement and exceptions, see
Obsługa wyjątków.
Instrukcja continue
, również pożyczona z C, kontynuuje następną iterację pętli:
>>> for num in range(2, 10):
... if num % 2 == 0:
... print("Found an even number", num)
... continue
... print("Found a number", num)
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9
4.5. pass
Statements¶
Instrukcja pass
nie robi nic. Można jej użyć, gdy składnia wymaga instrukcji a program nie wymaga działania. Na przykład:
>>> while True:
... pass # Busy-wait for keyboard interrupt (Ctrl+C)
...
Często jej się używa do tworzenia minimalnych klas:
>>> class MyEmptyClass:
... pass
...
Another place pass
can be used is as a place-holder for a function or
conditional body when you are working on new code, allowing you to keep thinking
at a more abstract level. The pass
is silently ignored:
>>> def initlog(*args):
... pass # Remember to implement this!
...
4.6. Definiowanie funkcji¶
Możemy stworzyć funkcję, która wypisuje ciąg Fibonacciego do wskazanej granicy:
>>> def fib(n): # write Fibonacci series up to n
... """Print a Fibonacci series up to n."""
... a, b = 0, 1
... while a < n:
... print(a, end=' ')
... a, b = b, a+b
... print()
...
>>> # Now call the function we just defined:
... fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
Słowo kluczowe def
oznacza definicję funkcji. Po nim musi następować nazwa funkcji oraz lista formalnych parametrów otoczona nawiasami. Instrukcje, które stanowią ciało funkcji zaczynają się w następnej linii i muszą być wcięte.
Opcjonalnie, pierwszy wiersz ciała funkcji może być gołym napisem (literałem): jest to tzw. napis dokumentujący lub inaczej docstring. (Więcej o docstringach znajdziesz w sekcji Napisy dokumentujące.) Istnieją pewne narzędzia, które używają docstringów do automatycznego tworzenia drukowanej lub dostępnej online dokumentacji albo pozwalają użytkownikowi na interaktywne przeglądanie kodu. Dobrym zwyczajem jest pisane napisów dokumentacyjnych w czasie pisania programu: spróbuj się do tego przyzwyczaić.
The execution of a function introduces a new symbol table used for the local
variables of the function. More precisely, all variable assignments in a
function store the value in the local symbol table; whereas variable references
first look in the local symbol table, then in the local symbol tables of
enclosing functions, then in the global symbol table, and finally in the table
of built-in names. Thus, global variables cannot be directly assigned a value
within a function (unless named in a global
statement), although they
may be referenced.
The actual parameters (arguments) to a function call are introduced in the local symbol table of the called function when it is called; thus, arguments are passed using call by value (where the value is always an object reference, not the value of the object). 1 When a function calls another function, a new local symbol table is created for that call.
A function definition introduces the function name in the current symbol table. The value of the function name has a type that is recognized by the interpreter as a user-defined function. This value can be assigned to another name which can then also be used as a function. This serves as a general renaming mechanism:
>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89
Przychodząc z innych języków, mógłbyś oponować, że fib
nie jest funkcją, ale procedurą, jako że nie zwraca wartości. Tak naprawdę nawet funkcje bez instrukcji return
zwracają wartość, chociaż dość nudną. Tę wartość nazywamy None
(to wbudowana nazwa). Wypisywanie wartości None
jest normalnie pomijane przez interpreter, jeśli miałaby to jedyna wypisywana wartość. Możesz ją zobaczyć, jeśli bardzo chcesz, używając print()
:
>>> fib(0)
>>> print(fib(0))
None
Prosto można napisać funkcję, która zwraca listę numerów ciągu Fibonnaciego zamiast go wyświetlać:
>>> def fib2(n): # return Fibonacci series up to n
... """Return a list containing the Fibonacci series up to n."""
... result = []
... a, b = 0, 1
... while a < n:
... result.append(a) # see below
... a, b = b, a+b
... return result
...
>>> f100 = fib2(100) # call it
>>> f100 # write the result
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
Ten przykład, jak zazwyczaj, prezentuje nowe cechy Pythona:
The
return
statement returns with a value from a function.return
without an expression argument returnsNone
. Falling off the end of a function also returnsNone
.Instrukcja
result.append(a)
wywołuje metodę listy obiektówresult
. Metoda to funkcja, która „należy” do obiektu i jest nazwanaobj.methodname
, gdzieobj
jest jakimś obiektem (może też być wyrażeniem) amethodname
jest nazwą metody, które jest zdefiniowana przez typ obiektu. Różne typy definiują różne metody. Metody różnych typów mogą mieć te same nazwy bez powodowania dwuznaczności. (Da się definiować własne typy obiektów i metody, używając klas, patrz Klasy.) Metodaappend()
pokazana w przykładzie jest zdefiniowana dla listy obiektów; dodaje nowy element na końcu listy. W tym przykładzie jest równoważnaresult = result + [a]
, ale bardziej wydajna.
4.7. Więcej o definiowaniu funkcji¶
Można też definiować funkcje ze zmienną liczbą argumentów. Są trzy sposoby, które można łączyć.
4.7.1. Domyślne wartości argumentów¶
Najbardziej przydatnym sposobem jest podanie domyślnej wartości dla jednego lub więcej argumentów. Tworzy to funkcję, która może zostać wywołana z mniejszą liczbą argumentów, niż jest podane w jej definicji. Na przykład:
def ask_ok(prompt, retries=4, reminder='Please try again!'):
while True:
ok = input(prompt)
if ok in ('y', 'ye', 'yes'):
return True
if ok in ('n', 'no', 'nop', 'nope'):
return False
retries = retries - 1
if retries < 0:
raise ValueError('invalid user response')
print(reminder)
Tę funkcję można wywołać na kilka sposobów:
podając tylko wymagany argument:
ask_ok('Do you really want to quit?')
podając jeden z opcjonalnych argumentów:
ask_ok('OK to overwrite the file?', 2)
lub podając wszystkie argumenty:
ask_ok('OK to overwrite file?', 2, 'Come on, only yes or no!')
Ten przykład wprowadza słowo kluczowe in
. Sprawdza ono, czy sekwencja zawiera szczególną wartość.
Wartości domyślne są ewaluowane w momencie definiowania funkcji w scopie defining, więc
i = 5
def f(arg=i):
print(arg)
i = 6
f()
wyświetli 5
.
Ważna uwaga: Wartość domyślna jest wyliczana tylko raz. Ma to znaczenie, gdy domyślna wartość jest obiektem mutowalnym takim jak lista, słownik lub instancje większości klas. Na przykład następująca funkcja akumuluje argumenty przekazane do niej w kolejnych wywołaniach:
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
To wyświetli
[1]
[1, 2]
[1, 2, 3]
Jeśli nie chcesz, żeby domyślna wartość była współdzielona pomiędzy kolejnymi wywołaniami, możesz napisać funkcję w ten sposób:
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
4.7.2. Argumenty nazwane¶
Funkcje mogą być również wywoływane przy użyciu argumentów nazwanych w formie kwarg=value
. Na przykład poniższa funkcja:
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")
akceptuje jeden wymagany argument (voltage
) i trzy opcjonalne argumenty (state
, action
i type
). Funkcja może być wywołana w dowolny z poniższych sposobów:
parrot(1000) # 1 positional argument
parrot(voltage=1000) # 1 keyword argument
parrot(voltage=1000000, action='VOOOOOM') # 2 keyword arguments
parrot(action='VOOOOOM', voltage=1000000) # 2 keyword arguments
parrot('a million', 'bereft of life', 'jump') # 3 positional arguments
parrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword
ale wszystkie poniższe wywołania byłyby niepoprawne:
parrot() # required argument missing
parrot(voltage=5.0, 'dead') # non-keyword argument after a keyword argument
parrot(110, voltage=220) # duplicate value for the same argument
parrot(actor='John Cleese') # unknown keyword argument
W wywołaniu funkcji argumenty nazwane muszą znajdować się za argumentami pozycyjnymi. Wszystkie przekazane argumenty nazwane muszą pasować do jednego argumentu akceptowanego przez funkcję (na przykład actor
nie jest poprawnym argumentem dla funkcji parrot
) a ich kolejność nie ma znaczenia. Dotyczy to również nie-opcjonalnych argumentów (na przykład parrot(voltage=1000)
też jest poprawne). Żaden argument nie może otrzymać wartości więcej niż raz. Tutaj jest przykład, który się nie powiedzie z powodu tego ograniczenia:
>>> def function(a):
... pass
...
>>> function(0, a=0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: function() got multiple values for keyword argument 'a'
When a final formal parameter of the form **name
is present, it receives a
dictionary (see Mapping Types — dict) containing all keyword arguments except for
those corresponding to a formal parameter. This may be combined with a formal
parameter of the form *name
(described in the next subsection) which
receives a tuple containing the positional arguments beyond the formal parameter
list. (*name
must occur before **name
.) For example, if we define a
function like this:
def cheeseshop(kind, *arguments, **keywords):
print("-- Do you have any", kind, "?")
print("-- I'm sorry, we're all out of", kind)
for arg in arguments:
print(arg)
print("-" * 40)
for kw in keywords:
print(kw, ":", keywords[kw])
Można ją wywołać w ten sposób:
cheeseshop("Limburger", "It's very runny, sir.",
"It's really very, VERY runny, sir.",
shopkeeper="Michael Palin",
client="John Cleese",
sketch="Cheese Shop Sketch")
i oczywiście wyświetli się nam:
-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
shopkeeper : Michael Palin
client : John Cleese
sketch : Cheese Shop Sketch
Zwróć uwagę, że kolejność w jakim argumenty są wyświetlane dokładnie odpowiada kolejności w jakim zostały one podane w wywołaniu funkcji.
4.7.3. Arbitralne listy argumentów¶
Najmniej często wykorzystywaną opcją jest specyfikowanie, że funkcja może być wywoływana z arbitralną liczbą argumentów. Takie argumenty zostaną opakowane w krotkę (zobacz Krotki i sekwencje). Przed zmienną liczbą argumentów, można wymusić jeden lub więcej argumentów.
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
Zazwyczaj takie wariadyczne
argumenty będą na koniec listy formalnych parametrów, ponieważ zbierają one wszystkie pozostałe argumenty przekazane funkcji. Każdy formalny argument po *args
może być «tylko-kluczowy», to znaczy da się go wprowadzić tylko poprzez słowo kluczowe a nie przez pozycję.
>>> def concat(*args, sep="/"):
... return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'
4.7.4. Rozpakowywanie listy argumentów¶
Odwrotna sytuacja wystąpi, gdy argumenty już są listą albo krotką a muszą być rozpakowane gdyż funkcja wymaga argumentów przekazanych pozycyjnie, jeden po drugim . Dla przykładu, wbudowana funkcja range()
oczekuje oddzielnych argumentów start oraz stop. Jeśli nie są dostępne oddzielnie, wywołaj funkcją z operatorem *
aby wypakować argumenty z listy lub krotki:
>>> list(range(3, 6)) # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> list(range(*args)) # call with arguments unpacked from a list
[3, 4, 5]
W podobny sposób, słowniki moga dostarczać argumentów kluczowych poprzez operator **
:
>>> def parrot(voltage, state='a stiff', action='voom'):
... print("-- This parrot wouldn't", action, end=' ')
... print("if you put", voltage, "volts through it.", end=' ')
... print("E's", state, "!")
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
4.7.5. Wyrażenia Lambda¶
Niewielkie anonimowe funkcje mogą byś tworzone z wykorzystaniem słowa kluczowego lambda
. Wykorzystując tą notację: lambda a, b: a+b
powstanie funkcja sumująca podane argumenty. Funkcje lambda mogą być wykorzystywane zawsze wtedy gdy potrzebne są objekty funkcji. Są synaktycznie ograniczone do jednego wyrażenia.
Semantycznie, jest to tylko lukier składniowy normalnej definicji funkcji. Podobnie jak funkcje zagnieżdżone funkcje lambda mogą odwoływać się do zmiennych z otaczającego zakresu
>>> def make_incrementor(n):
... return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43
Powyższy przykład wykorzystuje ekspresję lambda aby zwrócić funkcję. Inne wykorzystanie to przekazanie małej funkcji jako argumentu:
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
4.7.6. Napisy dokumentujące¶
Oto kilka konwencji dotyczących zawartości i formatowania ciągów dokumentacji.
Pierwsza linijka powinna zawierać zwięzłe streszczenie sensu jaki stoi za obiektem. Nie powinna wprost zawierać nazwy obiektu ani typu, gdyż te są dostępne w inny sposób (chyba, że nazwa jest czasownikiem opisujęcym działanie funkcji). Ta linijka powinna zaczynać się z dużej litery i kończyć kropką.
Jeśli w docstringu jest więcej niż jedna linijka, druga linijka powinna być pusta, aby rozdzielić ją od reszty opisu. Następne linijki powinny zawierać konwencje nazwenicze, efekty uboczne itd.
Parser Pythona nie usuwa wcięć z literału stringu wielolinijkowego, więc jeśli to jest pożądane, to narzędzia obrabiające dokumentację powinny usuwać wcięcia. Robi się to, wykorzystując następujące. Pierwsza nie-pusta linijka po pierwszej określa jak dużo wcięcia jest w całym docstringu. (Nie można do tego wykorzystać pierwszej linijki, ponieważ ta zazwyczaj przylega do cudzysłowów, więc sposób wcięcia jest nieoczywisty.) „Ilość wcięcia” z tej linijki jest następnie usuwana z tej i wszystkich następnych linijek. W kolejnych linijkach nie powinno być mniej wcięć ale jeśli tak będzie to całość wcięcia powinna być usunięta. Ilość wcięcia powinna być usuwana po zamianie tabulatorów na spacje (zazwyczaj na 8 spacji).
Poniżej przykład wielolinijkowego docstringu:
>>> def my_function():
... """Do nothing, but document it.
...
... No, really, it doesn't do anything.
... """
... pass
...
>>> print(my_function.__doc__)
Do nothing, but document it.
No, really, it doesn't do anything.
4.7.7. Adnotacje funkcji¶
Adnotacje funkcji to całkowicie opcjonalne metadane dające informacje o funkcjach zdefiniowanych przez użytkowników (zobacz PEP 3107 oraz PEP 484 aby uzyskać więcej informacji).
Annotations are stored in the __annotations__
attribute of the function
as a dictionary and have no effect on any other part of the function. Parameter
annotations are defined by a colon after the parameter name, followed by an
expression evaluating to the value of the annotation. Return annotations are
defined by a literal ->
, followed by an expression, between the parameter
list and the colon denoting the end of the def
statement. The
following example has a positional argument, a keyword argument, and the return
value annotated:
>>> def f(ham: str, eggs: str = 'eggs') -> str:
... print("Annotations:", f.__annotations__)
... print("Arguments:", ham, eggs)
... return ham + ' and ' + eggs
...
>>> f('spam')
Annotations: {'ham': <class 'str'>, 'return': <class 'str'>, 'eggs': <class 'str'>}
Arguments: spam eggs
'spam and eggs'
4.8. Intermezzo: Styl kodowania¶
Teraz, kiedy już jesteś gotowa aby pisać dłuższe, bardziej złożone Pythonowe dzieła, dobrze żebyśmy porozmawiali o stylu kodowania. Większość języków może być pisana (a mówiąc precyzyjniej, formatowana) w różnym stylu; bardziej lub mniej czytelnym. Zawsze dobrze jest dążyć, aby Twój kod był łatwy do czytania przez innych a w tym bardzo pomaga stosowanie fajnego stylu kodowania.
Dla Pythona PEP 8 stał się wzorcem stylu, którego trzyma się większość projektów; szerzy czytelny i miły dla oka styl kodowania. W którymś momencie, powinien go przeczytać każdy developer Pythona, poniżej przedstawiliśmy jego najistotniejsze elementy:
Jako wcięcie, wykorzystuj cztery spacje, nie tabulator.
Cztery spacje są dobrym kompromisem pomiędzy płytkim wcięciem (pozwala na więcej kroków zagnieżdżania ) a głębokim wcięciem (jest łatwiejsze do przeczytania). Tabulatorów najlepiej nie używać, wprowadzają zamiesznie.
Zawijaj linie tak, aby nie ich długość nie przekraczała 79 znaków.
To pomoże użytkownikom z małymi wyświetlaczami a na większych ekranach pozwoli mieć kilka plików obok siebie na ekranie.
Wstawiaj puste linie aby oddzielić od siebie funkcje, klasy lub większe bloki kodu wewnątrz funkcji.
Jeśli jest to możliwe, wstawiaj komentarze na oddzielnej lini.
Wykorzystuj docstringi
Korzystaj ze spacji naokoło operatorów oraz za przecinkami, ale nie przy nawiasach:
a = f(1, 2) + g(3, 4)
.Name your classes and functions consistently; the convention is to use
CamelCase
for classes andlower_case_with_underscores
for functions and methods. Always useself
as the name for the first method argument (see A First Look at Classes for more on classes and methods).Nie wykorzystuj ambitnych zestawów znaków (encoding) jeśli Twój kod będzie wykorzystywany międzynarodowo. Najepiej trzymać się domyślnych w Pythonie: UTF-8 lub nawet ASCII.
Podobnie, nie korzystaj ze znaków innych niż ASCII jako identyfikatorów, jeśli jest chociaż szansa, że osoby mówiące innym językiem będą czytać lub rozwijać Twój kod.
Przypisy
- 1
Tak właściwie, wywołanie przez referencję obiektu byłoby lepszym opisem, jako że jeśli mutowalny obiekt jest przekazany, wszystkie zmiany które wywoływany robi (elementy wstawiane na listę) będą widziane przez wywołującego.