Di Python, function bukanlah perintah istimewa yang berdiri di luar sistem tipe — function adalah object, sama seperti angka 42 dan string "kopi". Object function bisa disimpan ke variabel, dimasukkan ke list dan dictionary, dikirim sebagai argument, bahkan dikembalikan dari function lain. Bahasa dengan sifat ini disebut memperlakukan function sebagai first-class citizen. Status inilah yang membuat sorted(data, key=len) dan seluruh arsitektur callback di Python mungkin.

Function sebagai Nilai #

Menuliskan nama function tanpa tanda kurung menghasilkan object function itu sendiri — bukan hasil eksekusinya:

python
def sapa():
    return "Halo!"

salam = sapa
print(salam())
print(type(sapa))

Output yang Dihasilkan:

text
Halo!
<class 'function'>

Mari kita telaah apa yang terjadi pada baris-baris kode di atas:

  1. Baris 4 (salam = sapa): tanda kurung tidak ada, jadi yang diikat ke salam adalah object function sapa — bukan string hasil pemanggilan. Dua nama kini mengarah ke function yang sama, seperti dua label menempel pada satu kotak.
  2. Baris 5 (print(salam())): tanda kurung di sinilah eksekusi terjadi. Memanggil salam() sama persis dengan memanggil sapa().
  3. Baris 6 (print(type(sapa))): type() mengonfirmasi bahwa sapa adalah object dari class function — bukan "Halo!".

Function sebagai Argument #

Object function yang bisa dikirim sebagai argument membuka pola higher-order function: function yang menerima function lain dan menugaskannya. Perhatikan tampilkan berikut:

python
def teriak(teks):
    return teks.upper() + "!"

def bisik(teks):
    return teks.lower() + "..."

def tampilkan(teks, gaya):
    print(gaya(teks))

tampilkan("Perhatian", teriak)
tampilkan("Perhatian", bisik)

Output yang Dihasilkan:

text
PERHATIAN!
perhatian...

Pemanggilan tampilkan("Perhatian", teriak) mengirim object teriak — tanpa kurung — lewat parameter gaya. Di dalam badan function, gaya(teks) kemudian memanggil object tersebut dengan tanda kurung. Function tampilkan yang sama menghasilkan dua gaya keluaran berbeda hanya dengan berganti function yang dikirim. Argument key= pada sorted() yang kita pakai di Chapter Lambda Functions bekerja persis seperti ini.

Function dalam Koleksi #

Karena function adalah object, list dan dictionary boleh berisi function. Dictionary berisi function menjadi tabel keputusan yang menggantikan rantai if-elif:

python
def potongan_member(harga):
    return harga - 5000

def tanpa_potongan(harga):
    return harga

harga_promo = {"member": potongan_member, "umum": tanpa_potongan}

print(harga_promo["member"](50000))
print(harga_promo["umum"](50000))

Output yang Dihasilkan:

text
45000
50000

Dictionary harga_promo menyimpan dua object function sebagai value. Ekspresi harga_promo["member"] menghasilkan object function potongan_member, dan tanda kurung (50000) setelahnya langsung memanggilnya. Menambah strategi baru cukup menambah satu pasangan key-value — tanpa menyentuh kode yang sudah ada.

Function sebagai Nilai Balik #

Function juga bisa mengembalikan function. Function pembuat biasanya memilih salah satu dari beberapa function internal lalu mengirimkannya ke pemanggil:

python
def buat_pangkat(pangkat):
    def hitung(nilai):
        return nilai ** pangkat
    return hitung

kuadrat = buat_pangkat(2)
kubik = buat_pangkat(3)

print(kuadrat(5))
print(kubik(2))

Output yang Dihasilkan:

text
25
8

Mari kita telaah alurnya:

  1. Baris 5 (return hitung): buat_pangkat mengembalikan object function hitung — sekali lagi tanpa tanda kurung, jadi yang dikirim adalah function-nya, bukan hasil pemanggilannya.
  2. Baris 7 (kuadrat = buat_pangkat(2)): pangkat 2 tersimpan di namespace pembungkus, lalu function hitung yang mengingat nilai tersebut dikirim keluar. Setiap pemanggilan kuadrat(5) mengevaluasi 5 ** 2 menghasilkan 25.
  3. Baris 8 (kubik = buat_pangkat(3)): pemanggilan kedua membuat salinan berbeda yang mengingat pangkat 3 — inilah closure yang kita kenal pada Chapter local, global, dan nonlocal.

Jebakan Klasik: Lupa Kurung #

Kesalahan paling umum seputar first-class function adalah mencampuradukkan object function dengan hasil pemanggilannya. Bandingkan dua baris terakhir berikut:

python
>>> def ambil_poin(siswa):
...     return siswa[1]
...
>>> siswa = [("Budi", 85), ("Siti", 92)]
>>> sorted(siswa, key=ambil_poin)
[('Budi', 85), ('Siti', 92)]
>>> sorted(siswa, key=ambil_poin())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: ambil_poin() missing 1 required positional argument: 'siswa'

Baris key=ambil_poin mengirim object function untuk dipanggil oleh sorted() — inilah yang benar. Baris key=ambil_poin() mencoba mengeksekusi ambil_poin saat itu juga tanpa argument; Python mengevaluasi argument sebelum pemanggilan sorted() terjadi, dan ambil_poin() tanpa argument langsung memicu TypeError.