Fungsi di Python: def, return, argumen, *args dan **kwargs, lambda dan anotasi tipe
Fungsi di Python adalah blok kode bernama yang dideklarasikan dengan kata kunci def, dipanggil dengan nama dan tanda kurung, dan mengembalikan hasil lewat return; tanpa return, fungsi mengembalikan None. Argumen diberikan menurut urutan atau nama, *args mengumpulkan nilai posisional tambahan menjadi tuple, **kwargs mengumpulkan argumen bernama menjadi dict, dan lambda membuat fungsi anonim pendek dari satu ekspresi.
Fungsi di Python adalah potongan kode bernama yang bisa dipanggil berkali-kali dengan data masukan yang berbeda. Ia dideklarasikan dengan kata def, menerima nilai lewat parameter, dan mengembalikan hasil dengan return. Fungsi adalah cara utama untuk mengubah skrip panjang menjadi langkah-langkah yang jelas dan bisa diuji.
Cara mendeklarasikan fungsi: def python
Deklarasi fungsi diawali def, lalu nama, parameter dalam kurung, dan titik dua. Isinya ditulis menjorok empat spasi — indentasi di Python bukan hiasan, melainkan bagian dari sintaks.
def greet(name):
"""Mengembalikan sapaan untuk pengguna."""
return f"Halo, {name}!"
print(greet("Rina")) # Halo, Rina!
String dengan tanda kutip tiga tepat setelah judul disebut docstring, yaitu dokumentasi fungsi. Ia ditampilkan oleh help(greet) dan oleh petunjuk di editor. Menurut konvensi PEP 8, nama fungsi ditulis dalam snake_case: add_expense, get_rate.
Baris def greet(name): disebut signature fungsi: nama plus daftar parameter (dan anotasinya jika ada). Dari signature, pembaca langsung paham cara memanggil fungsi tanpa melihat isinya.
Apa yang dilakukan return di Python
return mengakhiri fungsi dan memberikan nilai kepada pemanggilnya. Kode setelah return yang sudah dijalankan tidak akan dieksekusi. Jika tidak ada return, atau ditulis tanpa nilai, fungsi mengembalikan None.
def area(w, h):
w * h # hasilnya dihitung lalu dibuang
print(area(3, 4)) # None
def area(w, h):
return w * h
print(area(3, 4)) # 12
Lupa menulis return adalah salah satu kesalahan paling umum pemula: fungsinya «jalan», tetapi mengembalikan None, dan masalahnya baru terasa di baris berikutnya. Fungsi juga bisa mengembalikan beberapa nilai — sebenarnya itu satu tuple yang mudah dibongkar:
def min_max(numbers):
return min(numbers), max(numbers)
low, high = min_max([4, 1, 9])
print(low, high) # 1 9
Return dini membantu mengurangi lapisan bersarang: kasus khusus disingkirkan dulu, baru logika utama ditulis.
def discount(price, is_member):
if price <= 0:
return 0
if not is_member:
return price
return price * 0.9
Argumen fungsi di Python: posisional dan bernama
Parameter adalah nama yang ditulis di deklarasi fungsi, sedangkan argumen adalah nilai yang diberikan saat pemanggilan. Argumen bisa diberikan menurut urutan (posisional) atau menurut nama (bernama). Argumen bernama membuat pemanggilan lebih mudah dibaca, dan urutannya tidak lagi penting.
def add_expense(title, amount, category="lainnya"):
return {"title": title, "amount": amount, "category": category}
add_expense("Kopi", 250) # posisional
add_expense(amount=250, title="Kopi") # menurut nama
add_expense("Taksi", 600, category="transport") # campuran
Parameter dengan nilai bawaan bersifat opsional. Aturannya: parameter dengan nilai bawaan ditulis setelah parameter wajib, dan saat pemanggilan argumen posisional ditulis sebelum argumen bernama. Jika tidak, Python menampilkan SyntaxError.
Jebakan: nilai bawaan yang bisa diubah
Nilai bawaan dihitung sekali — saat baris def dieksekusi, bukan setiap kali fungsi dipanggil. Jika nilainya list atau dict, objek itu akan dipakai bersama oleh semua pemanggilan.
def add_item(item, acc=[]):
acc.append(item)
return acc
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] — list mengingat pemanggilan sebelumnya
Cara yang benar adalah memakai None sebagai nilai bawaan dan membuat objek baru di dalam fungsi:
def add_item(item, acc=None):
if acc is None:
acc = []
acc.append(item)
return acc
*args dan **kwargs: jumlah argumen bebas
Tanda bintang di depan parameter mengumpulkan semua argumen posisional yang tersisa menjadi tuple (tuple), dan dua tanda bintang mengumpulkan semua argumen bernama menjadi dict (dict). Nama args dan kwargs hanya kebiasaan; yang penting adalah tanda bintangnya.
def total(*args):
return sum(args)
print(total(100, 250, 40)) # 390
def make_user(name, **kwargs):
user = {"name": name}
user.update(kwargs)
return user
print(make_user("Rina", city="Bandung", age=30))
# {'name': 'Rina', 'city': 'Bandung', 'age': 30}
Tanda bintang yang sama juga bekerja sebaliknya — saat pemanggilan, ia membongkar urutan atau dict menjadi argumen:
values = [3, 4]
print(pow(*values)) # 81
opts = {"sep": " | ", "end": "\n"}
print("a", "b", **opts) # a | b
Parameter hanya posisional dan hanya bernama
Garis miring / dan tanda bintang * dalam daftar parameter bekerja seperti palang: semua yang ada sebelum / hanya bisa diberikan menurut urutan (sintaksnya hadir sejak Python 3.8, PEP 570), dan semua yang ada setelah * hanya bisa diberikan menurut nama.
def convert(amount, /, *, rate, currency="USD"):
return round(amount / rate, 2), currency
convert(100, rate=90.5) # (1.1, 'USD')
convert(amount=100, rate=90.5) # TypeError: amount hanya boleh diberikan secara posisional
convert(100, 90.5) # TypeError: rate hanya boleh diberikan dengan nama
Parameter yang hanya bernama melindungi dari kebingungan pada pemanggilan seperti convert(100, 90.5, "EUR"), di mana tidak jelas apa nilai apa.
Ruang lingkup: aturan LEGB
Python mencari nama variabel secara berjenjang: Local (di dalam fungsi) → Enclosing (di fungsi luar) → Global (di tingkat modul) → Built-in (nama bawaan seperti len). Membaca variabel global di dalam fungsi boleh dilakukan, tetapi untuk memberinya nilai baru diperlukan deklarasi global.
limit = 1000
def check(amount):
return amount > limit # membaca global — boleh
def raise_limit():
global limit
limit = 2000 # tanpa global, akan terbentuk variabel lokal baru
Dalam praktiknya, hindari global: fungsi yang mengubah keadaan di luar dirinya sulit diuji. Lebih baik terima nilai lewat argumen dan kembalikan hasilnya.
Fungsi bersarang dan closure
Fungsi bisa dideklarasikan di dalam fungsi lain. Fungsi bersarang melihat variabel fungsi luarnya bahkan setelah fungsi luar selesai — inilah yang disebut closure. Untuk mengubah variabel seperti itu digunakan nonlocal.
def make_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
c = make_counter()
print(c(), c(), c()) # 1 2 3
Closure menjadi dasar decorator dan pabrik fungsi.
Fungsi juga objek
Di Python, fungsi adalah objek biasa: bisa disimpan di variabel, dikirim ke fungsi lain, dikembalikan dari fungsi, atau disimpan di dict. Fungsi punya atribut seperti __name__, __doc__, __defaults__, dan __annotations__.
def double(x):
return x * 2
f = double
print(f(5), f.__name__) # 10 double
print(list(map(double, [1, 2, 3]))) # [2, 4, 6]
Lambda di Python: fungsi anonim
Fungsi lambda adalah fungsi pendek tanpa nama yang badannya hanya satu ekspresi. Hasil ekspresi dikembalikan secara otomatis, sehingga return tidak ditulis.
square = lambda x: x ** 2
print(square(4)) # 16
expenses = [("Kopi", 250), ("Taksi", 600), ("Makan siang", 450)]
expenses.sort(key=lambda e: e[1], reverse=True)
print(expenses) # [('Taksi', 600), ('Makan siang', 450), ('Kopi', 250)]
print(list(filter(lambda n: n % 2 == 0, range(10)))) # [0, 2, 4, 6, 8]
Lambda cocok sebagai argumen singkat untuk sorted, max, map, dan filter. Menyimpan lambda ke variabel seperti square di atas tidak dianjurkan PEP 8: jika fungsi perlu nama, tulis def — ia akan punya __name__ yang normal di traceback dan tempat untuk docstring.
| def | lambda | |
|---|---|---|
| Nama | ada | tidak ada (<lambda>) |
| Badan | sejumlah instruksi | satu ekspresi |
| return | eksplisit | implisit |
| Docstring, anotasi | ada | tidak |
| Cocok untuk | segala keperluan | key atau callback singkat |
Anotasi tipe: petunjuk, bukan pemeriksaan
Anotasi menjelaskan tipe parameter dan hasil yang diharapkan. Python sendiri tidak memeriksanya saat program berjalan: fungsi dengan x: int tetap menerima string. Pemeriksaan dilakukan oleh alat luar — mypy, pyright, ty — dan oleh editor yang menyoroti kesalahan sebelum program dijalankan.
def add_expense(title: str, amount: float, category: str = "lainnya") -> dict[str, object]:
return {"title": title, "amount": amount, "category": category}
def area(w: float, h: float) -> float:
w * h # mypy: Missing return statement
Pemeriksaan bisa dijalankan tanpa instalasi dengan uvx mypy .. Contoh di atas memperlihatkan manfaat anotasi: return yang terlupa pada fungsi -> float langsung tertangkap analisis.
Sejak Python 3.12 (PEP 695), fungsi generik ditulis tanpa TypeVar: parameter tipe ditulis dalam kurung siku, dan alias tipe dideklarasikan dengan kata type.
def first[T](xs: list[T]) -> T:
return xs[0]
type Money = int | float
Di Python 3.14 (PEP 649) anotasi menjadi malas: ia dievaluasi saat pertama kali diakses, sehingga merujuk kelas yang dideklarasikan di bawah bisa dilakukan tanpa tanda kutip, dan from __future__ import annotations tidak lagi diperlukan.
«Tidak diberikan» atau «diberikan None»: sentinel di 3.15
Nilai bawaan None tidak memungkinkan kita membedakan «argumen tidak diberikan» dan «sengaja diberikan None». Bertahun-tahun masalah ini diselesaikan dengan penanda buatan sendiri:
_MISSING = object()
def get_rate(currency, default=_MISSING):
rates = {"USD": 90.5}
if currency in rates:
return rates[currency]
if default is _MISSING:
raise KeyError(currency)
return default
Di Python 3.15 hadir fungsi bawaan sentinel (PEP 661): MISSING = sentinel("MISSING") membuat penanda unik dengan repr yang jelas, yang tetap sama saat disalin, bisa di-serialisasi lewat pickle, dan bisa dipakai dalam anotasi seperti float | MISSING. Di versi lama, pola dengan object() tetap berfungsi.
Kesalahan umum dengan fungsi
- Lupa return — fungsi mengembalikan
None. - Lupa tanda kurung —
print(greet)menampilkan objek fungsi, bukan hasil pemanggilan. - Nilai bawaan yang bisa diubah — satu list dipakai bersama oleh semua pemanggilan.
- Jumlah argumen salah —
TypeError: greet() missing 1 required positional argument: 'name'. - Nama bertabrakan dengan bawaan — fungsi bernama
listatausumakan menutupi fungsi bawaan.
Ringkasan
Fungsi di Python dideklarasikan dengan def, mengembalikan hasil lewat return, dan menerima argumen menurut urutan atau nama. *args dan **kwargs memberi fleksibilitas, / dan * memberi ketegasan pada pemanggilan, lambda memberi keringkasan untuk fungsi sekali pakai, dan anotasi tipe bersama mypy menangkap kesalahan sebelum program dijalankan. Dalam kursus Python, topik ini dipraktikkan dengan cara memecah pencatat pengeluaran menjadi fungsi-fungsi beranotasi yang diperiksa mypy.
Pertanyaan umum
Apa beda parameter dan argumen?
Parameter adalah nama di deklarasi fungsi, argumen adalah nilai konkret yang diberikan saat pemanggilan. Pada def greet(name), name adalah parameter; pada greet("Rina"), string "Rina" adalah argumen.
Bisakah fungsi dipanggil sebelum dideklarasikan?
Di tingkat modul tidak bisa: baris def harus sudah dieksekusi sebelum pemanggilan, jika tidak akan terjadi NameError. Namun di dalam fungsi lain, kita bisa memanggil fungsi yang dideklarasikan di bawahnya, asalkan pemanggilan terjadi setelah deklarasinya dieksekusi.
Berapa banyak nilai yang bisa dikembalikan fungsi?
Secara formal satu, tetapi nilai itu bisa berupa tuple: return a, b mengembalikan tuple berisi dua elemen, yang bisa dibongkar menjadi x, y = f().
Apakah anotasi tipe wajib?
Tidak, Python berjalan tanpa anotasi. Namun pada kode yang dibaca orang lain, anotasi memudahkan perawatan dan memungkinkan mypy atau pyright menemukan kesalahan sebelum program dijalankan.
Bisakah lambda memakai if?
Hanya ekspresi kondisional: lambda x: "genap" if x % 2 == 0 else "ganjil". Instruksi, perulangan, dan penugasan tidak diperbolehkan di dalam lambda.
Topik ini dibahas mendalam dengan praktik di kursus “Python 3.15: от новичка до профи”.
Ke kursus