03 — Functions
Estimasi: 6 jam Tujuan: Function = abstraksi paling penting di programming. Setelah ini kamu bisa bikin function yang clean, reusable, dan Pythonic.
Kenapa Materi Ini Penting?
Setiap library AI yang akan kamu pakai pada akhirnya adalah kumpulan function: np.mean(), pd.read_csv(), model.fit(), tokenizer.encode(). Memahami function bukan cuma soal sintaks def, tapi soal berpikir modular — memecah masalah kompleks jadi unit-unit kecil yang bisa di-test dan di-reuse.
Function juga adalah pintu masuk ke konsep besar: pure function (foundation functional programming), higher-order function (yang dipakai di map/filter/reduce dan banyak ML utilities), dan eventually decorator (@staticmethod, @torch.no_grad, @app.route). Skill ini compound — semakin lihai pecah masalah jadi function, semakin produktif kamu di fase ML/LLM nanti.
Analogi besar: function = mesin di pabrik. Kamu masukkan bahan mentah lewat input (parameter), mesin proses sesuai resep (body), output produk jadi (return). Mesin yang sama bisa dipakai berulang-ulang dengan bahan berbeda.
Peta Konsep
flowchart TD
A[⚙️ Functions] --> B[📥 Parameters]
A --> C[📤 Return]
A --> D[🔍 Scope]
A --> E[🎁 Advanced]
B --> B1[Positional]
B --> B2[Keyword]
B --> B3[Default]
B --> B4[*args / **kwargs]
C --> C1[Single value]
C --> C2[Multiple tuple]
C --> C3[None implicit]
D --> D1[Local]
D --> D2[Enclosing]
D --> D3[Global]
D --> D4[Built-in]
E --> E1[Lambda]
E --> E2[Higher-order]
E --> E3[Type hints]
Bagian 1 — Kenapa Function?
Visualisasi: Function = Mesin
Cara Membaca Diagram
Kiri adalah input (argument), tengah adalah function body (logika), kanan adalah output (return value). Edge dari kiri ke kanan menunjukkan flow data. Function adalah black box: kasih input, terima output, tidak peduli detail dalam.
Walkthrough Step-by-Step
- Input — kamu kirim
berat=70dantinggi=1.7sebagai argument. - Function body — Python alokasi local scope, parameter
beratdantinggidi-bind ke nilai input. - Compute — eksekusi logika:
berat / (tinggi ** 2). - Return — kirim hasil
24.22keluar dari function. Local scope dibuang. - Output — caller (kode pemanggil) terima nilai untuk dipakai (assign ke variabel, print, dll).
Function ini bisa dipanggil ulang dengan input berbeda — itulah inti reusability.
Analogi Sehari-hari
Function = mesin jus. Kamu masukkan buah (input), tekan tombol, keluar jus (output). Mesin yang sama bisa dipakai untuk apel, jeruk, mangga — resep dalam mesin (function body) tetap, cuma bahan beda. Kalau lupa masukin bahan (lupa argument) → error. Kalau mesin rusak di tengah proses → exception.
Diagram statis Mermaid sebagai fallback:
flowchart LR
I1[📥 berat: 70] --> M[⚙️ hitung_bmi]
I2[📥 tinggi: 1.7] --> M
M --> O[📤 24.22]
Mesin ini bisa dipakai untuk siapa saja — Budi, Ani, Cici. Resep di dalam mesin tetap sama, hanya inputnya beda.
Code Tanpa Function
# Hitung BMI 3 orang
berat = 70
tinggi = 1.7
bmi = berat / (tinggi ** 2)
print(f"BMI: {bmi:.2f}")
berat = 80
tinggi = 1.75
bmi = berat / (tinggi ** 2)
print(f"BMI: {bmi:.2f}")
berat = 65
tinggi = 1.65
bmi = berat / (tinggi ** 2)
print(f"BMI: {bmi:.2f}")
Masalah:
- Repetitif
- Kalau rumus berubah, harus edit 3 tempat
- Sulit di-test
Code Dengan Function
def hitung_bmi(berat, tinggi):
return berat / (tinggi ** 2)
print(f"BMI: {hitung_bmi(70, 1.70):.2f}")
print(f"BMI: {hitung_bmi(80, 1.75):.2f}")
print(f"BMI: {hitung_bmi(65, 1.65):.2f}")
Keuntungan:
- DRY (Don't Repeat Yourself)
- Logic terpusat
- Mudah di-test
- Mudah di-baca
Aturan emas: kalau kamu copy-paste 3 kali, ubah jadi function.
Bagian 2 — Anatomi Function
def nama_function(parameter1, parameter2):
"""Docstring — penjelasan singkat function ini."""
# Body
hasil = parameter1 + parameter2
return hasil
def= keyword untuk define functionnama_function= nama (snake_case)parameter1, parameter2= input"""..."""= docstring (opsional tapi disarankan)return= output
Calling Function
hasil = nama_function(5, 3) # 8
print(hasil)
Function Tanpa Return
def greet(nama):
print(f"Halo, {nama}!")
greet("Yazid") # print "Halo, Yazid!"
hasil = greet("Ani") # hasil = None
Function tanpa return otomatis return None.
Bagian 3 — Parameter & Argument
Parameter = variabel di definisi function. Argument = nilai yang dikirim saat memanggil.
def tambah(a, b): # a, b = parameter
return a + b
tambah(5, 3) # 5, 3 = argument
Positional Argument
Berdasarkan urutan:
def info(nama, umur, kota):
print(f"{nama}, {umur} tahun, dari {kota}")
info("Budi", 25, "Bandung") # urut: nama, umur, kota
Keyword Argument
Sebutkan nama parameter:
info(nama="Budi", umur=25, kota="Bandung")
# Bisa diacak urutannya
info(kota="Bandung", umur=25, nama="Budi")
# Bisa campur, tapi positional dulu
info("Budi", kota="Bandung", umur=25)
Default Value
def greet(nama, salam="Halo"):
print(f"{salam}, {nama}!")
greet("Budi") # Halo, Budi!
greet("Ani", "Hi") # Hi, Ani!
greet("Cici", salam="Hey") # Hey, Cici!
Hati-hati: default value untuk mutable object (list, dict) bisa jadi bug. Akan dibahas di Common Pitfalls.
Bagian 4 — *args dan **kwargs
Untuk function yang menerima jumlah argument bervariasi.
*args — Banyak Positional Argument
def jumlah(*angka):
return sum(angka)
jumlah(1, 2, 3) # 6
jumlah(1, 2, 3, 4, 5) # 15
jumlah() # 0
*angka mengumpulkan semua argument jadi tuple.
**kwargs — Banyak Keyword Argument
def info(**data):
for key, value in data.items():
print(f"{key}: {value}")
info(nama="Budi", umur=25, kota="Bandung")
# nama: Budi
# umur: 25
# kota: Bandung
**data mengumpulkan semua keyword argument jadi dict.
Kombinasi
def fleksibel(a, b, *args, **kwargs):
print(f"a={a}, b={b}")
print(f"args={args}")
print(f"kwargs={kwargs}")
fleksibel(1, 2, 3, 4, 5, x=10, y=20)
# a=1, b=2
# args=(3, 4, 5)
# kwargs={'x': 10, 'y': 20}
Urutan parameter:
def f(positional, *args, default=value, **kwargs):
Contoh Praktis: Function Wrapper
def log_call(func_name, *args, **kwargs):
print(f"Calling {func_name}")
print(f" args: {args}")
print(f" kwargs: {kwargs}")
log_call("hitung_bmi", 70, 1.7, unit="metric")
Bagian 5 — Return Multiple Values
Python bisa return banyak nilai sekaligus (sebenarnya jadi tuple).
def stats(angka):
return min(angka), max(angka), sum(angka) / len(angka)
minimum, maksimum, rata = stats([1, 2, 3, 4, 5])
print(minimum, maksimum, rata) # 1 5 3.0
# Atau ambil sebagai tuple
hasil = stats([1, 2, 3, 4, 5])
print(hasil) # (1, 5, 3.0)
Skip Return Value yang Tidak Dipakai
_, _, rata = stats([1, 2, 3]) # cuma butuh rata
_ adalah konvensi untuk "tidak dipakai".
Bagian 6 — Scope (Cakupan Variabel)
Diagram LEGB
Cara Membaca Diagram
Python cari variabel berurutan dari Local → Enclosing → Global → Built-in. Edge solid menuju "Ketemu" (pakai value). Edge dashed menuju level berikutnya kalau tidak ditemukan. Kalau semua level kosong, NameError.
Walkthrough Step-by-Step
- Python ketemu nama variabel di kode (misal
x). - L (Local) — cek di scope function saat ini. Ada? Pakai. Tidak? Lanjut.
- E (Enclosing) — cek di function pembungkus (kalau nested). Ada? Pakai.
- G (Global) — cek di module level (top of file). Ada? Pakai.
- B (Built-in) — cek di built-in Python (
print,len, dll). Ada? Pakai. - Kalau tetap tidak ketemu →
NameError: name 'x' is not defined.
Analogi Sehari-hari
Cari kunci motor di rumah. Pertama cek kantong celana (local). Tidak ada? Cek tas yang lagi dibawa (enclosing). Tidak ada? Cek rak kunci di rumah (global). Tidak ada? Cek tempat umum / pos satpam (built-in). Begitu ketemu, berhenti cari. Kalau semua sudah dicek dan tidak ada → kunci hilang (NameError).
Diagram statis Mermaid sebagai fallback:
flowchart TD
A[🔍 Cari variabel x] --> L{Local scope?}
L -->|Ada| L1[✅ Pakai Local]
L -->|Tidak| E{Enclosing?}
E -->|Ada| E1[✅ Pakai Enclosing]
E -->|Tidak| G{Global?}
G -->|Ada| G1[✅ Pakai Global]
G -->|Tidak| B{Built-in?}
B -->|Ada| B1[✅ Pakai Built-in]
B -->|Tidak| X[❌ NameError]
Analogi: Python cari variabel kayak kamu cari kunci motor — kantong celana (local) → tas (enclosing) → rak rumah (global) → tempat umum (built-in). Begitu ketemu, berhenti cari.
Local Scope
def tambah(a, b):
hasil = a + b # variabel local
return hasil
print(tambah(2, 3)) # 5
print(hasil) # NameError! hasil hanya ada di dalam function
Global Scope
PI = 3.14159 # global
def luas_lingkaran(r):
return PI * r ** 2 # bisa baca global
print(luas_lingkaran(5))
Modifikasi Global (Hindari!)
counter = 0
def increment():
global counter # explicit declare
counter += 1
increment()
increment()
print(counter) # 2
Best practice: HINDARI modifikasi global. Pakai parameter dan return value. Global state = source of bugs.
LEGB Rule
Python cari variabel dengan urutan:
- Local — di dalam function saat ini
- Enclosing — function pembungkus
- Global — module level
- Built-in — built-in Python (
print,len, dll)
x = "global"
def luar():
x = "enclosing"
def dalam():
x = "local"
print(x) # local
dalam()
print(x) # enclosing
luar()
print(x) # global
Bagian 7 — Lambda (Anonymous Function)
Function singkat tanpa nama.
# Function biasa
def kuadrat(x):
return x ** 2
# Lambda equivalent
kuadrat = lambda x: x ** 2
print(kuadrat(5)) # 25
Kapan Pakai Lambda?
Saat butuh function kecil sekali pakai, biasanya sebagai argument:
data = [(1, "b"), (3, "a"), (2, "c")]
# Sort berdasarkan elemen ke-2
sorted_data = sorted(data, key=lambda item: item[1])
print(sorted_data) # [(3, 'a'), (1, 'b'), (2, 'c')]
nilai = [85, 60, 75, 90]
# Filter nilai >= 70
lulus = filter(lambda n: n >= 70, nilai)
print(list(lulus)) # [85, 75, 90]
# Mapping (kuadratkan)
kuadrat = map(lambda n: n ** 2, nilai)
print(list(kuadrat)) # [7225, 3600, 5625, 8100]
Aturan: kalau lambda sudah lebih dari 1 expression sederhana, pakai
defbiasa. Lambda untuk one-liner.
Bagian 8 — Type Hints (Penting di AI/Industry)
Python dinamis (tidak strict types), tapi kamu bisa kasih petunjuk tipe:
def hitung_bmi(berat: float, tinggi: float) -> float:
return berat / (tinggi ** 2)
def greet(nama: str, salam: str = "Halo") -> None:
print(f"{salam}, {nama}!")
def get_users() -> list[dict]:
return [{"id": 1, "name": "Budi"}]
Manfaat:
- IDE autocomplete lebih baik
- Bug lebih cepat ketahuan
- Code lebih self-documenting
- Standar di proyek profesional
Wajib pakai type hints. PyTorch, FastAPI, Pydantic, semua framework modern pakai. Kebiasaan ini akan membuatmu menonjol di bootcamp.
Bagian 9 — Common Pitfalls
Pitfall 1: Mutable Default Argument
# ❌ BAHAYA
def add_item(item, list_=[]): # default list di-share antar call!
list_.append(item)
return list_
print(add_item("a")) # ["a"]
print(add_item("b")) # ["a", "b"] ← bug!
# ✅ BENAR
def add_item(item, list_=None):
if list_ is None:
list_ = []
list_.append(item)
return list_
Pitfall 2: Lupa Return
def kuadrat(x):
x ** 2 # forget to return!
hasil = kuadrat(5)
print(hasil) # None — bug!
Pitfall 3: Modifikasi Argument
def tambah_item(lst, item):
lst.append(item) # modifikasi list yang dikirim!
return lst
original = [1, 2, 3]
tambah_item(original, 4)
print(original) # [1, 2, 3, 4] ← terjadi side effect
Best practice: function tidak modifikasi argument. Buat copy dulu kalau perlu.
Pitfall 4: Function Terlalu Panjang
# ❌ Function 100 baris yang ngapain banyak hal
def process_user(...):
# validate
# parse
# save to db
# send email
# log
# ...
# ✅ Pecah jadi function-function kecil
def validate_user(...): ...
def parse_user(...): ...
def save_user(...): ...
def notify_user(...): ...
def process_user(data):
user = parse_user(data)
if not validate_user(user):
return None
save_user(user)
notify_user(user)
return user
Single Responsibility Principle: satu function, satu tugas. Aturan dari Robert Martin's "Clean Code".
Bagian 10 — Higher-Order Functions
Diagram: Function Sebagai Bahan
flowchart LR
F1[⚙️ kuadrat] -->|jadi argument| H[⚙️ apply]
V[📥 value: 7] --> H
H --> O[📤 49]
Analogi: higher-order function = mesin yang menerima alat sebagai bagian inputnya. Bayangin mesin bor universal yang bisa pasang berbagai mata bor — mata bor itulah function yang di-pass.
Function yang menerima atau mengembalikan function. Penting di functional programming, banyak dipakai di ML.
Function Sebagai Argument
def apply(func, value):
return func(value)
apply(len, "hello") # 5
apply(lambda x: x ** 2, 7) # 49
Function Sebagai Return Value
def buat_pengali(n):
def pengali(x):
return x * n
return pengali
kali_2 = buat_pengali(2)
kali_5 = buat_pengali(5)
print(kali_2(10)) # 20
print(kali_5(10)) # 50
Ini disebut closure. Akan dibahas detail di file 09 (advanced).
Common Mistakes & FAQ
❌ Mistake 1: Lupa parantheses saat call
def hello():
print("hi")
hello # ❌ ini reference ke function, tidak panggil
hello() # ✅ panggil function
❌ Mistake 2: Variable shadowing
sum = 10 # ❌ override built-in `sum`
print(sum([1, 2, 3])) # TypeError: 'int' object is not callable
❌ Mistake 3: Bingung pass-by-reference vs value
def reset(x):
x = [] # ini cuma rebind local
original = [1, 2, 3]
reset(original)
print(original) # [1, 2, 3] — tidak berubah!
# Tapi modifikasi in-place beda:
def clear_inplace(x):
x.clear() # ini modify object yang sama
clear_inplace(original)
print(original) # []
Python pass reference. Kalau rebind dalam function, original tidak berubah. Kalau modify in-place, berubah.
❌ Mistake 4: Return di tengah loop tanpa exit
def find(data, target):
for x in data:
if x == target:
return True
return False # ❌ return setelah iter pertama!
# Fix: pindah return False keluar loop
def find(data, target):
for x in data:
if x == target:
return True
return False
FAQ
Q: Function harus ada return?
A: Tidak wajib. Tanpa return, otomatis return None. Tapi kalau function bertujuan komputasi (bukan side effect), wajib return.
Q: Berapa parameter ideal? A: Aturan umum: ≤ 3-4 parameter. Lebih dari itu, kemungkinan function terlalu banyak tugas → split, atau pakai dict/dataclass untuk grouping.
Q: Lambda bisa multi-line?
A: Tidak. Lambda hanya untuk satu expression. Multi-line → pakai def.
Q: Apa beda lambda dan def?
A: Lambda = anonymous, satu expression, return implicit. def = bisa multi-statement, ada nama, lebih mudah debug. Pakai def 95% kasus.
**Q: Kapan pakai *args vs kwargs? A:
*args= positional argument bervariasi (min(1, 2, 3))**kwargs= keyword argument bervariasi (dict(a=1, b=2))- Bisa kombinasi:
def f(*args, **kwargs)untuk fleksibilitas maksimum (sering di decorator).
Cek Pemahaman
- Bisa bedakan parameter dan argument?
- Tahu kapan pakai *args vs **kwargs?
- Bisa return multiple values?
- Paham LEGB scope rule?
- Tahu kapan pakai lambda vs def?
- Bisa pakai type hints di function?
- Tahu mutable default argument pitfall?
Challenge 2.3
Challenge 1 — Function Refactoring
Refactor kode ini jadi function:
# Hitung diskon untuk 3 produk
harga = 100000
diskon_persen = 20
diskon = harga * diskon_persen / 100
final = harga - diskon
print(f"Final: {final}")
harga = 250000
diskon_persen = 15
diskon = harga * diskon_persen / 100
final = harga - diskon
print(f"Final: {final}")
# (3 produk lagi serupa)
Bikin hitung_diskon(harga, diskon_persen) yang return final price. Pakai type hints.
Challenge 2 — Calculator Function-Based
Refactor calculator dari challenge sebelumnya jadi pakai function:
tambah(a, b),kurang(a, b),kali(a, b),bagi(a, b)- 1 function
calculate(op, a, b)yang panggil function di atas - Validasi: kalau op tidak valid, return None
Challenge 3 — *args Practice
Bikin function statistik(*nilai) yang return tuple (min, max, avg, count).
print(statistik(85, 60, 75, 90, 55))
# (55, 90, 73.0, 5)
Challenge 4 — **kwargs Practice
Bikin function buat_user(**data) yang validasi:
- Wajib ada key:
nama,email - Optional:
umur,kota - Kalau tidak ada wajib, return error message
- Kalau valid, return dict user
Challenge 5 — Lambda + sorted
Diberikan list of dict:
users = [
{"nama": "Budi", "umur": 25, "skor": 85},
{"nama": "Ani", "umur": 30, "skor": 92},
{"nama": "Cici", "umur": 22, "skor": 78},
]
Pakai lambda + sorted untuk:
- Sort berdasarkan umur
- Sort berdasarkan skor descending
- Sort berdasarkan panjang nama
Challenge 6 — Higher-Order Function
Bikin function apply_to_all(func, items) yang aplikasikan func ke setiap item dan return list hasilnya.
print(apply_to_all(lambda x: x ** 2, [1, 2, 3, 4]))
# [1, 4, 9, 16]
Challenge 7 — Function Sebagai Pipeline
Bikin pipeline yang:
bersihkan(text)— lowercase, strip whitespacehitung_kata(text)— return jumlah katakata_unik(text)— return list kata unik (set lalu list)
Lalu bikin process(text, *funcs) yang aplikasikan semua function berturut-turut.
text = " Hello World HELLO Python WORLD "
result = process(text, bersihkan, kata_unik, len)
print(result) # 3 (hello, world, python)
Hint: pakai for loop untuk apply functions.
Challenge 8 — Type Hints
Tambahkan type hints lengkap ke function-function di challenge sebelumnya. Run mypy (install dengan pip install mypy) untuk cek.
Selanjutnya: 04-data-structures.md — list, dict, set, tuple plus comprehension. Inti manipulasi data di Python.