4. Lebih Banyak Alat Pengatur Aliran Control Flow

As well as the while statement just introduced, Python uses a few more that we will encounter in this chapter.

4.1. Pernyataan if

Mungkin tipe pernyataan yang paling terkenal adalah pernyataan if. Sebagai contoh:

>>> 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

Mungkin ada nol atau lebih bagian elif, dan bagian else adalah opsional. Kata kunci 'elif' adalah kependekan dari 'else if', dan berguna untuk menghindari indentasi yang berlebihan. Sebuah if ... elif ... elif ... adalah urutan pengganti untuk pernyataan switch atau case yang ditemukan dalam bahasa lain.

If you're comparing the same value to several constants, or checking for specific types or attributes, you may also find the match statement useful. For more details see match Statements.

4.2. Pernyataan for

Pernyataan for dalam Python sedikit berbeda dari apa yang mungkin Anda gunakan di C atau Pascal. Alih-alih selalu mengulangi perkembangan angka dalam aritmatika (seperti dalam Pascal), atau memberikan pengguna kemampuan untuk menentukan langkah iterasi dan kondisi berhenti (seperti C), Python pernyataan for diulangi pada item-item dari urutan apa pun (daftar list atau string), dalam urutan yang muncul dalam urutan. Misalnya (tidak ada permainan kata-kata):

>>> # Measure some strings:
>>> words = ['cat', 'window', 'defenestrate']
>>> for w in words:
...     print(w, len(w))
...
cat 3
window 6
defenestrate 12

Kode yang memodifikasi koleksi collection sambil mengulangi koleksi yang sama bisa sulit untuk diperbaiki. Sebagai gantinya, biasanya lebih mudah untuk mengulang salinan koleksi atau membuat koleksi baru:

# Create a sample collection
users = {'Hans': 'active', 'Éléonore': 'inactive', '景太郎': 'active'}

# Strategy:  Iterate over a copy
for user, status in users.copy().items():
    if status == 'inactive':
        del users[user]

# Strategy:  Create a new collection
active_users = {}
for user, status in users.items():
    if status == 'active':
        active_users[user] = status

4.3. Fungsi range()

Jika Anda perlu mengulangi urutan angka, fungsi bawaan range() berguna. Ini menghasilkan urutan pregressions aritmatika:

>>> for i in range(5):
...     print(i)
...
0
1
2
3
4

Titik akhir yang diberikan tidak pernah menjadi bagian dari urutan yang dihasilkan; range(10) menghasilkan 10 nilai, indeks sah legal untuk item dengan urutan panjang 10. Dimungkinkan untuk membiarkan rentang mulai dari nomor lain, atau untuk menentukan kenaikan yang berbeda (bahkan negatif; kadang-kadang ini disebut 'step'):

>>> list(range(5, 10))
[5, 6, 7, 8, 9]

>>> list(range(0, 10, 3))
[0, 3, 6, 9]

>>> list(range(-10, -100, -30))
[-10, -40, -70]

Untuk beralih pada indeks urutan, Anda dapat menggabungkan range() dan len() sebagai berikut:

>>> 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

Dalam kebanyakan kasus seperti itu, bagaimanapun, lebih mudah untuk menggunakan fungsi enumerate(), lihat Teknik Perulangan.

Hal aneh terjadi jika Anda hanya mencetak rentang range:

>>> range(10)
range(0, 10)

Dalam banyak hal objek dikembalikan oleh range() berperilaku seolah-olah itu adalah daftar list, tetapi sebenarnya tidak. Ini adalah objek yang mengembalikan item berurutan dari urutan yang diinginkan ketika Anda mengulanginya, tetapi itu tidak benar-benar membuat daftar list, sehingga menghemat ruang.

Kami mengatakan bahwa objek seperti itu adalah iterable, yaitu, cocok sebagai target untuk fungsi dan konstruksi yang mengharapkan sesuatu dari mana mereka dapat memperoleh item berturut-turut sampai pasokan habis. Kita telah melihat bahwa pernyataan for adalah konstruksi seperti itu, sedangkan contoh fungsi yang membutuhkan sebuah iterable adalah sum():

>>> sum(range(4))  # 0 + 1 + 2 + 3
6

Later we will see more functions that return iterables and take iterables as arguments. In chapter Struktur Data, we will discuss in more detail about list().

4.4. break and continue Statements

The break statement breaks out of the innermost enclosing for or while loop:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print(f"{n} equals {x} * {n//x}")
...             break
...
4 equals 2 * 2
6 equals 2 * 3
8 equals 2 * 4
9 equals 3 * 3

The continue statement continues with the next iteration of the loop:

>>> for num in range(2, 10):
...     if num % 2 == 0:
...         print(f"Found an even number {num}")
...         continue
...     print(f"Found an odd number {num}")
...
Found an even number 2
Found an odd number 3
Found an even number 4
Found an odd number 5
Found an even number 6
Found an odd number 7
Found an even number 8
Found an odd number 9

4.5. else Clauses on Loops

In a for or while loop the break statement may be paired with an else clause. If the loop finishes without executing the break, the else clause executes.

In a for loop, the else clause is executed after the loop finishes its final iteration, that is, if no break occurred.

In a while loop, it's executed after the loop's condition becomes false.

In either kind of loop, the else clause is not executed if the loop was terminated by a break. Of course, other ways of ending the loop early, such as a return or a raised exception, will also skip execution of the else clause.

This is exemplified in the following for 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

(Yes, this is the correct code. Look closely: the else clause belongs to the for loop, not the if statement.)

One way to think of the else clause is to imagine it paired with the if inside the loop. As the loop executes, it will run a sequence like if/if/if/else. The if is inside the loop, encountered a number of times. If the condition is ever true, a break will happen. If the condition is never true, the else clause outside the loop will execute.

When used with a loop, the else clause has more in common with the else clause of a try statement than it does with 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 Menangani Pengecualian.

4.6. Pernyataan pass

Pernyataan pass tidak melakukan apa-apa. Ini dapat digunakan ketika pernyataan diperlukan secara sintaksis tetapi program tidak memerlukan tindakan. Sebagai contoh:

>>> while True:
...     pass  # Busy-wait for keyboard interrupt (Ctrl+C)
...

Ini biasanya digunakan untuk membuat kelas minimal:

>>> class MyEmptyClass:
...     pass
...

Tempat lain pass dapat digunakan adalah sebagai tempat-penampung place-holder untuk fungsi atau badan bersyarat conditional body saat Anda bekerja pada kode baru, memungkinkan Anda untuk terus berpikir pada tingkat yang lebih abstrak. pass diabaikan secara diam-diam:

>>> def initlog(*args):
...     pass   # Remember to implement this!
...

4.7. match Statements

A match statement takes an expression and compares its value to successive patterns given as one or more case blocks. This is superficially similar to a switch statement in C, Java or JavaScript (and many other languages), but it's more similar to pattern matching in languages like Rust or Haskell. Only the first pattern that matches gets executed and it can also extract components (sequence elements or object attributes) from the value into variables.

The simplest form compares a subject value against one or more literals:

def http_error(status):
    match status:
        case 400:
            return "Bad request"
        case 404:
            return "Not found"
        case 418:
            return "I'm a teapot"
        case _:
            return "Something's wrong with the internet"

Note the last block: the "variable name" _ acts as a wildcard and never fails to match. If no case matches, none of the branches is executed.

You can combine several literals in a single pattern using | ("or"):

case 401 | 403 | 404:
    return "Not allowed"

Patterns can look like unpacking assignments, and can be used to bind variables:

# point is an (x, y) tuple
match point:
    case (0, 0):
        print("Origin")
    case (0, y):
        print(f"Y={y}")
    case (x, 0):
        print(f"X={x}")
    case (x, y):
        print(f"X={x}, Y={y}")
    case _:
        raise ValueError("Not a point")

Study that one carefully! The first pattern has two literals, and can be thought of as an extension of the literal pattern shown above. But the next two patterns combine a literal and a variable, and the variable binds a value from the subject (point). The fourth pattern captures two values, which makes it conceptually similar to the unpacking assignment (x, y) = point.

If you are using classes to structure your data you can use the class name followed by an argument list resembling a constructor, but with the ability to capture attributes into variables:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

def where_is(point):
    match point:
        case Point(x=0, y=0):
            print("Origin")
        case Point(x=0, y=y):
            print(f"Y={y}")
        case Point(x=x, y=0):
            print(f"X={x}")
        case Point():
            print("Somewhere else")
        case _:
            print("Not a point")

You can use positional parameters with some builtin classes that provide an ordering for their attributes (e.g. dataclasses). You can also define a specific position for attributes in patterns by setting the __match_args__ special attribute in your classes. If it's set to ("x", "y"), the following patterns are all equivalent (and all bind the y attribute to the var variable):

Point(1, var)
Point(1, y=var)
Point(x=1, y=var)
Point(y=var, x=1)

A recommended way to read patterns is to look at them as an extended form of what you would put on the left of an assignment, to understand which variables would be set to what. Only the standalone names (like var above) are assigned to by a match statement. Dotted names (like foo.bar), attribute names (the x= and y= above) or class names (recognized by the "(...)" next to them like Point above) are never assigned to.

Patterns can be arbitrarily nested. For example, if we have a short list of Points, with __match_args__ added, we could match it like this:

class Point:
    __match_args__ = ('x', 'y')
    def __init__(self, x, y):
        self.x = x
        self.y = y

match points:
    case []:
        print("No points")
    case [Point(0, 0)]:
        print("The origin")
    case [Point(x, y)]:
        print(f"Single point {x}, {y}")
    case [Point(0, y1), Point(0, y2)]:
        print(f"Two on the Y axis at {y1}, {y2}")
    case _:
        print("Something else")

We can add an if clause to a pattern, known as a "guard". If the guard is false, match goes on to try the next case block. Note that value capture happens before the guard is evaluated:

match point:
    case Point(x, y) if x == y:
        print(f"Y=X at {x}")
    case Point(x, y):
        print(f"Not on the diagonal")

Several other key features of this statement:

  • Like unpacking assignments, tuple and list patterns have exactly the same meaning and actually match arbitrary sequences. An important exception is that they don't match iterators or strings.

  • Sequence patterns support extended unpacking: [x, y, *rest] and (x, y, *rest) work similar to unpacking assignments. The name after * may also be _, so (x, y, *_) matches a sequence of at least two items without binding the remaining items.

  • Mapping patterns: {"bandwidth": b, "latency": l} captures the "bandwidth" and "latency" values from a dictionary. Unlike sequence patterns, extra keys are ignored. An unpacking like **rest is also supported. (But **_ would be redundant, so it is not allowed.)

  • Subpatterns may be captured using the as keyword:

    case (Point(x1, y1), Point(x2, y2) as p2): ...
    

    will capture the second element of the input as p2 (as long as the input is a sequence of two points)

  • Most literals are compared by equality, however the singletons True, False and None are compared by identity.

  • Patterns may use named constants. These must be dotted names to prevent them from being interpreted as capture variable:

    from enum import Enum
    class Color(Enum):
        RED = 'red'
        GREEN = 'green'
        BLUE = 'blue'
    
    color = Color(input("Enter your choice of 'red', 'blue' or 'green': "))
    
    match color:
        case Color.RED:
            print("I see red!")
        case Color.GREEN:
            print("Grass is green")
        case Color.BLUE:
            print("I'm feeling the blues :(")
    

For a more detailed explanation and additional examples, you can look into PEP 636 which is written in a tutorial format.

4.8. Mendefinisikan Fungsi

Kita dapat membuat fungsi yang menulis seri Fibonacci ke batas acak arbitrary:

>>> def fib(n):    # write Fibonacci series less than n
...     """Print a Fibonacci series less than 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

Kata kunci def memperkenalkan fungsi definition. Itu harus diikuti oleh nama fungsi dan daftar parameter formal yang di dalam tanda kurung. Pernyataan yang membentuk tubuh fungsi mulai dari baris berikutnya, dan harus diberi indentasi.

Pernyataan pertama dari tubuh fungsi secara opsional dapat berupa string literal; string literal ini adalah string dokumentasi fungsi, atau docstring. (Lebih lanjut tentang docstring dapat ditemukan di bagian String Dokumentasi.) Ada alat yang menggunakan docstring untuk secara otomatis menghasilkan dokumentasi online atau cetak, atau untuk membiarkan pengguna menelusuri kode secara interaktif; itu praktik yang baik untuk memasukkan dokumen dalam kode yang Anda tulis, jadi biasakan seperti itu.

execution dari suatu fungsi memperkenalkan tabel simbol baru yang digunakan untuk variabel lokal dari fungsi tersebut. Lebih tepatnya, semua tugas variabel dalam suatu fungsi menyimpan nilai dalam tabel simbol lokal; sedangkan referensi variabel pertama-tama terlihat pada tabel simbol lokal, kemudian pada tabel simbol lokal lampiran enclosing fungsi, kemudian pada tabel simbol global, dan akhirnya pada tabel nama bawaan. Dengan demikian, variabel global dan variabel lampiran enclosing fungsi tidak dapat secara langsung menetapkan nilai dalam suatu fungsi (kecuali, untuk variabel global, disebutkan dalam pernyataan global, atau, untuk variabel lampiran enclosing fungsi, dinamai dalam pernyataan nonlocal), meskipun mungkin direferensikan.

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, or calls itself recursively, a new local symbol table is created for that call.

Definisi fungsi mengasosiasikan nama fungsi dengan objek fungsi dalam tabel simbol saat ini. Sebuah interpreter dapat mengenali objek yang ditunjuk dengan nama itu sebagai fungsi yang ditentukan oleh pengguna. Nama lain juga dapat menunjuk ke objek fungsi yang sama dan juga dapat digunakan untuk mengakses fungsi tersebut:

>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89

Berasal dari bahasa lain, Anda mungkin keberatan bahwa fib bukan fungsi melainkan prosedur karena tidak mengembalikan nilai. Bahkan, fungsi bahkan tanpa pernyataan return mengembalikan nilai, meskipun yang agak membosankan. Nilai ini disebut None (ini adalah nama bawaan). Menulis nilai None biasanya dihilangkan suppressed oleh interpreter jika itu akan menjadi satu-satunya nilai yang ditulis. Anda dapat melihatnya jika Anda benar-benar ingin menggunakan print():

>>> fib(0)
>>> print(fib(0))
None

Sangat mudah untuk menulis fungsi yang mengembalikan daftar list nomor seri Fibonacci, alih-alih mencetaknya:

>>> 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]

Contoh ini, seperti biasa, menunjukkan beberapa fitur Python baru:

  • Pernyataan return kembali dengan nilai dari suatu fungsi. return tanpa argumen ekspresi mengembalikan None. Keluar dari akhir suatu fungsi juga mengembalikan None.

  • The statement result.append(a) calls a method of the list object result. A method is a function that 'belongs' to an object and is named obj.methodname, where obj is some object (this may be an expression), and methodname is the name of a method that is defined by the object's type. Different types define different methods. Methods of different types may have the same name without causing ambiguity. (It is possible to define your own object types and methods, using classes, see Classes) The method append() shown in the example is defined for list objects; it adds a new element at the end of the list. In this example it is equivalent to result = result + [a], but more efficient.

4.9. Lebih lanjut tentang Mendefinisikan Fungsi

Dimungkinkan juga untuk mendefinisikan fungsi dengan sejumlah variabel argumen. Ada tiga bentuk, yang bisa digabungkan.

4.9.1. Nilai Argumen Bawaan

Bentuk yang paling berguna adalah menentukan nilai bawaan untuk satu atau lebih argumen. Ini menciptakan fungsi yang bisa dipanggil dengan argumen yang lebih sedikit daripada yang didefinisikan untuk diizinkan. Sebagai contoh:

def ask_ok(prompt, retries=4, reminder='Please try again!'):
    while True:
        reply = input(prompt)
        if reply in {'y', 'ye', 'yes'}:
            return True
        if reply in {'n', 'no', 'nop', 'nope'}:
            return False
        retries = retries - 1
        if retries < 0:
            raise ValueError('invalid user response')
        print(reminder)

Fungsi ini dapat dipanggil dengan beberapa cara:

  • hanya memberikan argumen wajib: ask_ok('Do you really want to quit?')

  • memberikan salah satu argumen opsional: ask_ok('OK to overwrite the file?', 2)

  • atau bahkan memberikan semua argumen: ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')

Contoh ini juga memperkenalkan kata kunci in. Ini menguji apakah suatu urutan berisi nilai tertentu atau tidak.

Nilai bawaan dievaluasi pada titik definisi fungsi dalam lingkup defining, sehingga:

i = 5

def f(arg=i):
    print(arg)

i = 6
f()

akan mencetak 5.

Peringatan penting: Nilai bawaan dievaluasi hanya sekali. Ini membuat perbedaan ketika bawaan adalah objek yang dapat diubah seperti daftar list, kamus dictionary, atau instances dari sebagian besar kelas. Misalnya, fungsi berikut mengakumulasi argumen yang diteruskan pada panggilan berikutnya:

def f(a, L=[]):
    L.append(a)
    return L

print(f(1))
print(f(2))
print(f(3))

Ini akan mencetak:

[1]
[1, 2]
[1, 2, 3]

Jika Anda tidak ingin bawaan dibagi dengan panggilan berikutnya, Anda dapat menulis fungsi seperti ini sebagai gantinya:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

4.9.2. Argumen Kata Kunci Keyword Arguments

Fungsi juga dapat dipanggil menggunakan keyword argument dari bentuk kwarg=value. Misalnya, fungsi berikut:

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, "!")

menerima satu argumen yang diperlukan (voltage) dan tiga argumen opsional (state, action, dan type). Fungsi ini dapat dipanggil dengan salah satu cara berikut:

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

tetapi semua pemanggilan berikut ini tidak valid:

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

Dalam pemanggilan fungsi, argumen kata kunci keyword argument harus mengikuti argumen posisi. Semua argumen kata kunci keyword argument yang diteruskan harus cocok dengan salah satu argumen yang diterima oleh fungsi (mis. actor bukan argumen yang valid untuk fungsi parrot), dan urutannya tidak penting. Ini juga termasuk argumen non-opsional (mis. parrot(voltage=1000) juga valid). Tidak ada argumen yang dapat menerima nilai lebih dari sekali. Berikut ini contoh yang gagal karena batasan ini:

>>> 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 argument 'a'

Ketika parameter formal terakhir dari bentuk **name ada, ia menerima kamus dictionary (lihat Mapping Types --- dict) yang berisi semua argumen kata kunci keyword argument kecuali yang terkait dengan parameter formal. Ini dapat digabungkan dengan parameter formal dari bentuk *name (dijelaskan dalam subbagian berikutnya) yang menerima tuple yang berisi argumen posisi di luar daftar parameter formal. (*name harus ada sebelum **name.) Misalnya, jika kita mendefinisikan fungsi seperti ini:

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])

Ini bisa disebut seperti ini:

cheeseshop("Limburger", "It's very runny, sir.",
           "It's really very, VERY runny, sir.",
           shopkeeper="Michael Palin",
           client="John Cleese",
           sketch="Cheese Shop Sketch")

dan tentu saja itu akan mencetak:

-- 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

Perhatikan bahwa bagaimana urutan argumen kata kunci dicetak telah dijamin sesuai dengan urutan yang disediakan dalam pemanggilan fungsi.

4.9.3. Parameter spesial

Secara bawaan, argumen dapat diteruskan ke fungsi Python baik dengan posisi atau secara eksplisit oleh kata kunci. Untuk keterbacaan dan kinerja, masuk akal untuk membatasi cara argumen dapat dilewatkan sehingga pengembang hanya perlu melihat definisi fungsi untuk menentukan apakah item dilewatkan secara posisi saja, posisi atau kata kunci, atau kata kunci saja.

Definisi fungsi mungkin terlihat seperti:

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
      -----------    ----------     ----------
        |             |                  |
        |        Positional or keyword   |
        |                                - Keyword only
         -- Positional only

di mana / dan * adalah opsional. Jika digunakan, simbol-simbol ini menunjukkan jenis parameter dengan cara argumen dilewatkan ke fungsi: posisi-saja, posisi-atau-kata kunci, dan kata kunci-saja. Parameter kata kunci juga disebut sebagai parameter bernama.

4.9.3.1. Argumen Posisi-atau-Kata Kunci

Jika / dan * tidak ada dalam definisi fungsi, argumen dapat diteruskan ke fungsi dengan posisi atau kata kunci.

4.9.3.2. Parameter Posisi-saja

Melihat ini sedikit lebih detail, dimungkinkan untuk menandai parameter tertentu sebagai positional-only. Jika positional-only, urutan parameter penting, dan parameter tidak dapat dilewatkan dengan kata kunci. Parameter posisi-saja ditempatkan sebelum / (garis miring). / Digunakan untuk secara logis memisahkan parameter posisi-saja dari parameter lainnya. Jika tidak ada / dalam definisi fungsi, tidak ada parameter posisi-saja.

Parameter yang mengikuti / dapat berupa positional-or-keyword atau keyword-only.

4.9.3.3. Argumen Kata Kunci-saja

Untuk menandai parameter sebagai keyword-only, yang menunjukkan parameter harus dilewatkan dengan argumen kata kunci, tempatkan * dalam daftar argumen tepat sebelum parameter keyword-only.

4.9.3.4. Contoh Fungsi

Perhatikan definisi fungsi contoh berikut dengan memperhatikan marker / dan *:

>>> def standard_arg(arg):
...     print(arg)
...
>>> def pos_only_arg(arg, /):
...     print(arg)
...
>>> def kwd_only_arg(*, arg):
...     print(arg)
...
>>> def combined_example(pos_only, /, standard, *, kwd_only):
...     print(pos_only, standard, kwd_only)

Definisi fungsi pertama, standard_arg, bentuk yang paling akrab, tidak menempatkan batasan pada konvensi pemanggilan dan argumen dapat dilewatkan dengan posisi atau kata kunci:

>>> standard_arg(2)
2

>>> standard_arg(arg=2)
2

Fungsi kedua pos_only_arg dibatasi hanya menggunakan parameter posisi karena ada / dalam definisi fungsi:

>>> pos_only_arg(1)
1

>>> pos_only_arg(arg=1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: pos_only_arg() got some positional-only arguments passed as keyword arguments: 'arg'

The third function kwd_only_arg only allows keyword arguments as indicated by a * in the function definition:

>>> kwd_only_arg(3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given

>>> kwd_only_arg(arg=3)
3

Dan yang terakhir menggunakan ketiga konvensi pemanggilan dalam definisi fungsi yang sama:

>>> combined_example(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: combined_example() takes 2 positional arguments but 3 were given

>>> combined_example(1, 2, kwd_only=3)
1 2 3

>>> combined_example(1, standard=2, kwd_only=3)
1 2 3

>>> combined_example(pos_only=1, standard=2, kwd_only=3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: combined_example() got some positional-only arguments passed as keyword arguments: 'pos_only'

Akhirnya, pertimbangkan definisi fungsi ini yang memiliki potensi tabrakan antara argumen posisi name dan **kwds yang memiliki name sebagai kunci:

def foo(name, **kwds):
    return 'name' in kwds

Tidak ada kemungkinan panggilan yang memungkinkan untuk mengembalikan ke dalam ``True'' karena kata kunci `` 'nama' 'akan selalu terikat ke parameter pertama. Sebagai contoh:

>>> foo(1, **{'name': 2})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() got multiple values for argument 'name'
>>>

Tetapi menggunakan / (argumen posisi saja), ini dimungkinkan karena memungkinkan name sebagai argumen posisi dan 'name' sebagai kunci dalam argumen kata kunci keyword argument:

>>> def foo(name, /, **kwds):
...     return 'name' in kwds
...
>>> foo(1, **{'name': 2})
True

Dengan kata lain, nama-nama parameter posisi-saja dapat digunakan dalam **kwds tanpa ambiguitas.

4.9.3.5. Rekap

Contoh kasus dimana akan menentukan parameter mana yang akan digunakan dalam definisi fungsi:

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):

Sebagai pedoman:

  • Gunakan posisi-saja jika Anda ingin nama parameter tidak tersedia bagi pengguna. Ini berguna ketika nama parameter tidak memiliki arti nyata, jika Anda ingin menegakkan urutan argumen ketika fungsi dipanggil atau jika Anda perlu mengambil beberapa parameter posisi dan kata kunci bergantian arbitrary.

  • Gunakan kata kunci-saja ketika nama memiliki makna dan definisi fungsi lebih mudah dipahami dengan secara eksplisit menggunakan nama atau Anda ingin mencegah pengguna mengandalkan posisi argumen yang dikirimkan.

  • Untuk API, gunakan posisi-saja untuk mencegah perubahan yang merusak dari API jika nama parameter diubah di masa mendatang.

4.9.4. Daftar Argumen Berubah-ubah Arbitrary

Akhirnya, opsi yang paling jarang digunakan adalah menentukan bahwa suatu fungsi dapat dipanggil dengan sejumlah argumen acak arbitrary. Argumen-argumen ini akan dibungkus dalam sebuah tuple (lihat tuttuples). Sebelum jumlah variabel argumen, nol atau lebih argumen normal dapat muncul.

def write_multiple_items(file, separator, *args):
    file.write(separator.join(args))

Normally, these variadic arguments will be last in the list of formal parameters, because they scoop up all remaining input arguments that are passed to the function. Any formal parameters which occur after the *args parameter are 'keyword-only' arguments, meaning that they can only be used as keywords rather than positional arguments.

>>> def concat(*args, sep="/"):
...     return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'

4.9.5. Pembukaan Paket Unpacking Daftar Argumen

Situasi sebaliknya terjadi ketika argumen sudah ada dalam daftar list atau tuple tetapi perlu dibongkar untuk panggilan fungsi yang membutuhkan argumen posisi terpisah. Sebagai contoh, fungsi bawaan range() mengharapkan argumen terpisah start dan stop. Jika tidak tersedia secara terpisah, tulis fungsi panggilan dengan operator-* untuk membongkar argumen dari daftar list atau tuple:

>>> 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]

Dengan cara yang sama, kamus dapat mengirimkan argumen kata kunci dengan 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.9.6. Ekspresi Lambda

Fungsi kecil anonim dapat dibuat dengan kata kunci lambda. Fungsi ini mengembalikan jumlah dari dua argumennya: lambda a, b: a+b. Fungsi Lambda dapat digunakan di mana pun objek fungsi diperlukan. Mereka secara sintaksis terbatas pada satu ekspresi. Secara semantik, mereka hanya pemanis sintaksis untuk definisi fungsi normal. Seperti definisi fungsi bersarang, fungsi lambda dapat mereferensikan variabel dari cakupan yang mengandung

>>> def make_incrementor(n):
...     return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43

Contoh di atas menggunakan ekspresi lambda untuk mengembalikan fungsi. Penggunaan lain adalah untuk melewatkan fungsi kecil sebagai argumen:

>>> 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.9.7. String Dokumentasi

Berikut adalah beberapa konvensi tentang konten dan format string dokumentasi.

Baris pertama harus selalu berupa ringkasan singkat dan ringkas dari tujuan objek. Untuk singkatnya, itu tidak boleh secara eksplisit menyatakan nama atau jenis objek, karena ini tersedia dengan cara lain (kecuali jika nama tersebut merupakan kata kerja yang menggambarkan operasi fungsi). Baris ini harus dimulai dengan huruf kapital dan diakhiri dengan titik.

Jika ada lebih banyak baris dalam string dokumentasi, baris kedua harus kosong, memisahkan ringkasan secara visual dari sisa deskripsi. Baris berikut harus satu atau lebih paragraf yang menggambarkan konvensi pemanggilan objek, efek sampingnya, dll.

Pengurai Python tidak menghapus lekukan dari string multi-baris literal di Python, jadi alat yang memproses dokumentasi harus menghapus indentasi jika diinginkan. Ini dilakukan dengan menggunakan konvensi berikut. Baris tidak-kosong pertama setelah baris pertama string menentukan jumlah indentasi untuk seluruh string dokumentasi. (Kami tidak dapat menggunakan baris pertama karena umumnya berbatasan dengan tanda kutip pembukaan string sehingga indentasinya tidak terlihat dalam string literal.) Spasi "equivalent" untuk indentasi ini kemudian dihilangkan dari awal semua baris string. Baris yang indentasi lebih sedikit seharusnya tidak terjadi, tetapi jika terjadi semua spasi whitespace utama harus dihilangkan. Kesetaraan spasi harus diuji setelah ekspansi tab (hingga 8 spasi, biasanya).

Berikut adalah contoh dari multi-baris docstring:

>>> 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.9.8. Anotasi Fungsi

Function annotations informasi metadata yang sepenuhnya opsional tentang jenis yang digunakan oleh fungsi yang ditentukan pengguna (lihat PEP 3107 dan PEP 484 untuk informasi lebih lanjut).

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 required argument, an optional 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.10. Intermezzo: Gaya Coding

Sekarang Anda akan menulis potongan Python yang lebih panjang dan lebih kompleks, ini adalah saat yang tepat untuk berbicara tentang coding style. Sebagian besar bahasa dapat ditulis (atau lebih ringkas, formatted) dalam gaya yang berbeda; beberapa lebih mudah dibaca daripada yang lain. Memudahkan orang lain untuk membaca kode Anda selalu merupakan ide yang baik, dan mengadopsi gaya pengkodean yang bagus sangat membantu untuk itu.

Untuk Python, PEP 8 telah muncul sebagai panduan gaya yang dipatuhi sebagian besar proyek; itu mempromosikan gaya pengkodean yang sangat mudah dibaca dan menyenangkan. Setiap pengembang Python harus membacanya di beberapa bagian; di sini adalah poin paling penting yang ditunjukkan untuk Anda:

  • Gunakan lekukan 4-spasi, dan tanpa tab.

    4 spasi adalah kompromi yang baik antara indentasi kecil (memungkinkan kedalaman bersarang lebih besar) dan indentasi besar (lebih mudah dibaca). Tab menimbulkan kebingungan, dan sebaiknya ditinggalkan.

  • Bungkus wrap garis agar tidak melebihi 79 karakter.

    Ini membantu pengguna dengan tampilan kecil dan memungkinkan untuk memiliki beberapa file kode berdampingan pada tampilan yang lebih besar.

  • Gunakan baris kosong untuk memisahkan fungsi dan kelas, dan blok kode yang lebih besar di dalam fungsi.

  • Jika memungkinkan, berikan komentar pada baris terkait.

  • Gunakan String Dokumentasi docstrings.

  • Gunakan spasi di sekitar operator dan setelah koma, tetapi tidak secara langsung di dalam konstruksi kurung bracketing: a = f(1, 2) + g(3, 4).

  • Beri nama kelas dan fungsi Anda secara konsisten; konvensi ini menggunakan UpperCamelCase untuk kelas dan lowercase_with_underscores untuk fungsi dan metode. Selalu gunakan self sebagai nama untuk argumen metode pertama (lihat tut-firstclass untuk lebih lanjut tentang kelas dan metode).

  • Jangan gunakan pengkodean ajaib fancy encodings jika kode Anda dimaksudkan untuk digunakan di lingkungan internasional. Default Python, UTF-8, atau bahkan ASCII biasa berfungsi paling baik dalam hal apa pun.

  • Demikian juga, jangan gunakan karakter non-ASCII dalam pengidentifikasi jika hanya ada sedikit kesempatan orang berbicara bahasa yang berbeda akan membaca atau merawat kode.

Catatan kaki