20. Parameter dan Argumen Fungsi
Di Bab 19, kita belajar cara mendefinisikan dan memanggil fungsi(function) dengan parameter dasar. Sekarang kita akan mengeksplorasi sistem parameter dan argumen Python yang fleksibel secara mendalam. Memahami mekanisme ini memungkinkan kamu menulis fungsi yang sekaligus kuat dan mudah digunakan.
20.1) Argumen Positional dan Keyword
Saat kamu memanggil sebuah fungsi, kamu bisa mengoper argumen dengan dua cara mendasar: berdasarkan posisi atau berdasarkan nama (keyword).
20.1.1) Argumen Positional
Argumen positional dicocokkan dengan parameter berdasarkan urutannya. Argumen pertama masuk ke parameter pertama, argumen kedua ke parameter kedua, dan seterusnya.
def calculate_discount(price, discount_percent):
"""Calculate the final price after applying a discount."""
discount_amount = price * (discount_percent / 100)
final_price = price - discount_amount
return final_price
# Mengoper argumen berdasarkan posisi
result = calculate_discount(100, 20)
print(result)Output:
80.0Dalam contoh ini, 100 ditetapkan ke price dan 20 ke discount_percent murni berdasarkan posisinya dalam pemanggilan fungsi.
Urutan sangat krusial pada argumen positional:
# Contoh: Kita ingin menghitung item seharga $100 dengan diskon 20%
# Urutan benar: price dulu, lalu discount
print(calculate_discount(100, 20))
# Urutan salah: discount dulu, lalu price
print(calculate_discount(20, 100))Output:
80.0
-16.0Saat kamu menukar argumen, Python tidak tahu kamu membuat kesalahan—Python hanya menetapkannya sesuai urutan. Ini menghasilkan nilai yang secara matematis valid tetapi secara logika salah (harga negatif!).
20.1.2) Argumen Keyword
Argumen keyword secara eksplisit menyebutkan parameter mana yang menerima nilai tertentu dengan menggunakan nama parameter diikuti tanda sama dengan dan nilainya. Ini membuat kode kamu lebih mudah dibaca dan melindungi dari kesalahan urutan.
def create_user_profile(username, email, age):
"""Create a user profile with the given information."""
profile = f"User: {username}\nEmail: {email}\nAge: {age}"
return profile
# Menggunakan argumen keyword
profile = create_user_profile(username="alice_smith", email="alice@example.com", age=28)
print(profile)Output:
User: alice_smith
Email: alice@example.com
Age: 28Dengan argumen keyword, urutan tidak berpengaruh:
# Hasil sama, urutan berbeda
profile1 = create_user_profile(username="bob", email="bob@example.com", age=35)
profile2 = create_user_profile(age=35, username="bob", email="bob@example.com")
profile3 = create_user_profile(email="bob@example.com", age=35, username="bob")
# Ketiganya menghasilkan output yang identik
print(profile1 == profile2 == profile3)Output:
TrueFleksibilitas ini sangat berharga terutama saat sebuah fungsi punya banyak parameter, sehingga mudah melihat nilai mana yang sesuai dengan parameter mana.
20.1.3) Mencampur Argumen Positional dan Keyword
Kamu bisa menggabungkan kedua gaya ini dalam satu pemanggilan fungsi, tetapi ada aturan penting: argumen positional harus muncul sebelum argumen keyword.
def format_address(street, city, state, zip_code):
"""Format a mailing address."""
return f"{street}\n{city}, {state} {zip_code}"
# Valid: argumen positional dulu, lalu argumen keyword
address = format_address("123 Main St", "Springfield", state="IL", zip_code="62701")
print(address)Output:
123 Main St
Springfield, IL 62701Di sini, "123 Main St" dan "Springfield" adalah positional (ditetapkan ke street dan city), sementara state dan zip_code ditentukan berdasarkan nama.
Mencoba menaruh argumen positional setelah argumen keyword akan menyebabkan error:
# Tidak valid: argumen positional setelah argumen keyword
# address = format_address(street="123 Main St", "Springfield", state="IL", zip_code="62701")
# SyntaxError: positional argument follows keyword argumentPython menegakkan aturan ini karena setelah kamu mulai menggunakan argumen keyword, menjadi ambigu parameter positional mana yang harus diisi oleh argumen tanpa nama berikutnya.
20.1.4) Kapan Menggunakan Masing-Masing Gaya
Gunakan argumen positional saat:
- Fungsi memiliki sedikit parameter (biasanya 1-3)
- Urutan parameter jelas dan intuitif
- Fungsi sering dipakai dan urutannya sudah umum diketahui
# Jelas dan ringkas
print(len("hello"))
result = max(10, 20, 5)Gunakan argumen keyword saat:
- Fungsi memiliki banyak parameter
- Makna parameter tidak langsung jelas
- Kamu ingin melewati beberapa parameter yang punya nilai default (dibahas berikutnya)
- Kamu ingin membuat kode kamu self-documenting
# Jelas dan eksplisit
user = create_user_profile(username="charlie", email="charlie@example.com", age=42)20.2) Nilai Default Parameter
Fungsi dapat menentukan nilai default untuk parameter. Saat pemanggil tidak memberikan argumen untuk parameter yang punya default, Python menggunakan nilai default tersebut.
20.2.1) Mendefinisikan Parameter dengan Default
Nilai default ditentukan dalam definisi fungsi menggunakan operator penugasan:
def greet_user(name, greeting="Hello"):
"""Greet a user with a customizable greeting."""
return f"{greeting}, {name}!"
# Menggunakan greeting default
print(greet_user("Alice"))
# Memberikan greeting kustom
print(greet_user("Bob", "Good morning"))
print(greet_user("Carol", greeting="Hi"))Output:
Hello, Alice!
Good morning, Bob!
Hi, Carol!Parameter greeting memiliki nilai default "Hello". Saat kamu memanggil greet_user("Alice"), Python memakai default ini. Saat kamu memberikan argumen kedua, nilainya menimpa default.
20.2.2) Parameter dengan Default Harus Setelah Parameter Wajib
Python mewajibkan parameter yang punya nilai default muncul setelah semua parameter tanpa default. Aturan ini mencegah ambiguitas tentang argumen mana yang sesuai dengan parameter mana.
# Benar: parameter wajib dulu, lalu default
def create_product(name, price, category="General", in_stock=True):
"""Create a product record."""
return {
"name": name,
"price": price,
"category": category,
"in_stock": in_stock
}
product = create_product("Laptop", 999.99)
print(product)Output:
{'name': 'Laptop', 'price': 999.99, 'category': 'General', 'in_stock': True}Mencoba menaruh parameter wajib setelah parameter dengan default akan menyebabkan syntax error:
# Tidak valid: parameter wajib setelah parameter default
# def invalid_function(name="Unknown", age):
# return f"{name} is {age} years old"
# SyntaxError: non-default argument follows default argumentIni masuk akal: jika name punya default tetapi age tidak, bagaimana Python tahu apakah invalid_function(25) berarti name=25 dengan age tidak diisi, atau age=25 dengan name memakai default? Aturan ini menghilangkan ambiguitas tersebut.
20.2.3) Kegunaan Praktis Parameter Default
Parameter default sangat bagus untuk fungsi di mana argumen tertentu jarang berubah:
def calculate_shipping(weight, distance, express=False):
"""Calculate shipping cost based on weight and distance."""
base_rate = 0.50 * weight + 0.10 * distance
if express:
base_rate *= 2 # Biaya pengiriman express menjadi dua kali lipat
return round(base_rate, 2)
# Kebanyakan pengiriman adalah standar
standard_cost = calculate_shipping(5, 100)
print(f"Standard: ${standard_cost}")
# Sesekali ada yang butuh express
express_cost = calculate_shipping(5, 100, express=True)
print(f"Express: ${express_cost}")Output:
Standard: $12.5
Express: $25.0Desain ini membuat kasus yang paling umum (pengiriman standar) mudah dipanggil, sambil tetap mendukung kasus yang lebih jarang (express) saat diperlukan.
20.2.4) Banyak Default dan Penimpaan Selektif
Saat sebuah fungsi punya beberapa parameter dengan default, kamu bisa menimpa kombinasi apa pun dari mereka menggunakan argumen keyword:
def format_currency(amount, currency="USD", show_symbol=True, decimal_places=2):
"""Format a number as currency."""
symbols = {"USD": "$", "EUR": "€", "GBP": "£", "JPY": "¥"}
formatted = f"{amount:.{decimal_places}f}"
if show_symbol and currency in symbols:
formatted = f"{symbols[currency]}{formatted}"
return formatted
# Menggunakan semua default
print(format_currency(42.5))
# Menimpa hanya currency
print(format_currency(42.5, currency="EUR"))
# Menimpa beberapa default
print(format_currency(42.5, currency="JPY", decimal_places=0))Output:
$42.50
€42.50
¥42Fleksibilitas ini memungkinkan pemanggil menyesuaikan tepat apa yang dibutuhkan sambil menjaga pemanggilan fungsi tetap ringkas.
20.3) Daftar Argumen Panjang Variabel dengan *args
Terkadang kamu ingin fungsi menerima jumlah argumen berapa pun tanpa tahu sebelumnya akan ada berapa. Python menyediakan *args untuk tujuan ini.
20.3.1) Memahami *args
Sintaks *args dalam daftar parameter mengumpulkan semua argumen positional tambahan menjadi sebuah tuple. Nama args adalah konvensi (singkatan dari "arguments"), tetapi kamu bisa memakai nama parameter valid apa pun setelah tanda bintang.
def calculate_total(*numbers):
"""Calculate the sum of any number of values."""
total = 0
for num in numbers:
total += num
return total
# Bisa dengan jumlah argumen berapa pun
print(calculate_total(10))
print(calculate_total(10, 20))
print(calculate_total(10, 20, 30, 40))
print(calculate_total())Output:
10
30
100
0Di dalam fungsi, numbers adalah tuple yang berisi semua argumen positional yang dioper ke fungsi. Saat tidak ada argumen yang diberikan, itu adalah tuple kosong.
20.3.2) Menggabungkan Parameter Biasa dengan *args
Kamu bisa punya parameter biasa sebelum *args. Parameter biasa mengonsumsi beberapa argumen pertama, dan *args mengumpulkan sisanya:
def create_team(team_name, *members):
"""Create a team with a name and any number of members."""
member_list = ", ".join(members)
return f"Team {team_name}: {member_list}"
# Argumen pertama masuk ke team_name, sisanya masuk ke members
print(create_team("Alpha", "Alice", "Bob"))
print(create_team("Beta", "Carol"))
print(create_team("Gamma", "Dave", "Eve", "Frank", "Grace"))Output:
Team Alpha: Alice, Bob
Team Beta: Carol
Team Gamma: Dave, Eve, Frank, GraceArgumen pertama ("Alpha", "Beta", atau "Gamma") ditetapkan ke team_name, dan semua argumen sisanya dikumpulkan ke dalam tuple members.
20.4) Parameter Keyword-Only dan **kwargs
Python menyediakan dua mekanisme tambahan untuk menangani argumen: parameter keyword-only dan **kwargs untuk mengumpulkan argumen keyword yang sewenang-wenang.
20.4.1) Parameter Keyword-Only
Parameter keyword-only harus ditentukan menggunakan argumen keyword—parameter ini tidak bisa dioper secara positional. Kamu membuatnya dengan menempatkannya setelah * atau setelah *args dalam daftar parameter.
def create_account(username, *, email, age):
"""Create an account. Email and age must be specified by name."""
return {
"username": username,
"email": email,
"age": age
}
# Benar: email dan age ditentukan dengan keyword
account = create_account("alice", email="alice@example.com", age=28)
print(account)
# Tidak valid: mencoba mengoper email dan age secara positional
# account = create_account("bob", "bob@example.com", 30)
# TypeError: create_account() takes 1 positional argument but 3 were givenOutput:
{'username': 'alice', 'email': 'alice@example.com', 'age': 28}Tanda * dalam daftar parameter bertindak sebagai pemisah. Segala sesuatu setelahnya harus dioper sebagai argumen keyword. Ini berguna saat kamu ingin memaksa pemanggil agar eksplisit tentang parameter tertentu, sehingga kode lebih mudah dibaca dan lebih kecil kemungkinan salah.
Kamu juga bisa menggabungkan parameter biasa, *args, dan parameter keyword-only:
def log_event(event_type, *details, severity="INFO", timestamp=None):
"""Log an event with optional details and metadata."""
# Kita akan mempelajari modul datetime secara detail di Bab 39,
# tapi untuk sekarang, cukup tahu bahwa baris-baris ini mengambil waktu saat ini
# dan memformatnya sebagai string timestamp
from datetime import datetime
if timestamp is None:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
details_str = " | ".join(details)
return f"[{timestamp}] {severity}: {event_type} - {details_str}"
# event_type adalah positional, details dikumpulkan oleh *details,
# severity dan timestamp adalah keyword-only
print(log_event("Login", "User: alice", "IP: 192.168.1.1"))
print(log_event("Error", "Database connection failed", severity="ERROR"))Output (timestamp akan bervariasi tergantung kapan kamu menjalankan kode):
[2025-12-18 19:29:16] INFO: Login - User: alice | IP: 192.168.1.1
[2025-12-18 19:29:16] ERROR: Error - Database connection failed20.4.2) Memahami **kwargs
Sintaks **kwargs mengumpulkan semua argumen keyword tambahan menjadi sebuah dictionary. Seperti args, nama kwargs adalah konvensi (singkatan dari "keyword arguments"), tetapi kamu bisa memakai nama valid apa pun setelah tanda bintang ganda.
def create_product(**attributes):
"""Create a product with any number of attributes."""
product = {}
for key, value in attributes.items():
product[key] = value
return product
# Oper argumen keyword apa pun yang kamu mau
laptop = create_product(name="Laptop", price=999.99, brand="TechCorp", in_stock=True)
print(laptop)
phone = create_product(name="Phone", price=699.99, color="Black")
print(phone)Output:
{'name': 'Laptop', 'price': 999.99, 'brand': 'TechCorp', 'in_stock': True}
{'name': 'Phone', 'price': 699.99, 'color': 'Black'}Di dalam fungsi, attributes adalah dictionary di mana key adalah nama parameter dan value adalah argumen yang dioper.
20.4.3) Menggabungkan Parameter Biasa, *args, dan **kwargs
Kamu bisa menggunakan semua mekanisme ini bersama-sama, tetapi mereka harus muncul dalam urutan tertentu:
- Parameter positional biasa
*args(jika ada)- Parameter keyword-only (jika ada)
**kwargs(jika ada)
def complex_function(required, *args, keyword_only, **kwargs):
"""Demonstrate all parameter types together."""
print(f"Required: {required}")
print(f"Args: {args}")
print(f"Keyword-only: {keyword_only}")
print(f"Kwargs: {kwargs}")
complex_function(
"value1", # required
"value2", "value3", # args
keyword_only="kw", # keyword_only
extra1="e1", # kwargs
extra2="e2" # kwargs
)Output:
Required: value1
Args: ('value2', 'value3')
Keyword-only: kw
Kwargs: {'extra1': 'e1', 'extra2': 'e2'}Fleksibilitas ini kuat, tetapi sebaiknya digunakan dengan bijak. Kebanyakan fungsi tidak membutuhkan semua mekanisme ini.
20.4.4) Kasus Penggunaan Praktis: Fungsi Konfigurasi
Penggunaan umum untuk **kwargs adalah membuat fungsi yang menerima opsi konfigurasi:
def connect_to_database(host, port, **options):
"""Connect to a database with flexible configuration options."""
connection_string = f"Connecting to {host}:{port}"
# Proses opsi tambahan apa pun
if options.get("ssl"):
connection_string += " with SSL"
if options.get("timeout"):
connection_string += f" (timeout: {options['timeout']}s)"
if options.get("pool_size"):
connection_string += f" (pool size: {options['pool_size']})"
return connection_string
# Koneksi dasar
print(connect_to_database("localhost", 5432))
# Dengan SSL
print(connect_to_database("db.example.com", 5432, ssl=True))
# Dengan beberapa opsi
print(connect_to_database("db.example.com", 5432, ssl=True, timeout=30, pool_size=10))Output:
Connecting to localhost:5432
Connecting to db.example.com:5432 with SSL
Connecting to db.example.com:5432 with SSL (timeout: 30s) (pool size: 10)Pola ini memungkinkan fungsi menerima jumlah parameter konfigurasi opsional berapa pun tanpa mendefinisikannya semua secara eksplisit dalam daftar parameter.
20.5) Unpacking Argumen Saat Memanggil Fungsi
Sama seperti *args dan **kwargs mengumpulkan argumen saat mendefinisikan fungsi, kamu bisa menggunakan * dan ** untuk membongkar (unpack) koleksi saat memanggil fungsi.
20.5.1) Unpacking Sequence dengan *
Operator * membongkar sebuah sequence (list, tuple, dll.) menjadi argumen positional terpisah:
def calculate_rectangle_area(width, height):
"""Calculate the area of a rectangle."""
return width * height
# Alih-alih mengoper argumen satu per satu
dimensions = [5, 10]
area = calculate_rectangle_area(dimensions[0], dimensions[1])
print(area)
# Bongkar list secara langsung
area = calculate_rectangle_area(*dimensions)
print(area)Output:
50
50Saat kamu menulis *dimensions, Python membongkar list [5, 10] menjadi dua argumen terpisah, seolah-olah kamu menulis calculate_rectangle_area(5, 10).
Ini bekerja dengan iterable apa pun:
def format_name(first, middle, last):
"""Format a full name."""
return f"{first} {middle} {last}"
# Unpacking sebuah tuple
name_tuple = ("John", "Q", "Public")
print(format_name(*name_tuple))
# Unpacking sebuah list
name_list = ["Jane", "M", "Doe"]
print(format_name(*name_list))
# Bahkan unpacking sebuah string (setiap karakter menjadi argumen)
# Ini hanya bekerja jika fungsi mengharapkan jumlah argumen yang tepat
def show_first_three(a, b, c):
return f"{a}, {b}, {c}"
print(show_first_three(*"ABC"))Output:
John Q Public
Jane M Doe
A, B, C20.5.2) Unpacking Dictionary dengan **
Operator ** membongkar sebuah dictionary menjadi argumen keyword:
def create_user(username, email, age):
"""Create a user profile."""
return f"User: {username}, Email: {email}, Age: {age}"
# Dictionary dengan key yang cocok dengan nama parameter
user_data = {
"username": "alice",
"email": "alice@example.com",
"age": 28
}
# Bongkar dictionary
profile = create_user(**user_data)
print(profile)Output:
User: alice, Email: alice@example.com, Age: 28Saat kamu menulis **user_data, Python membongkar dictionary menjadi argumen keyword, setara dengan:
create_user(username="alice", email="alice@example.com", age=28)Key pada dictionary harus cocok dengan nama parameter fungsi, atau kamu akan mendapat error:
# Tidak valid: key dictionary tidak cocok dengan nama parameter
invalid_data = {"name": "bob", "email": "bob@example.com", "age": 30}
# profile = create_user(**invalid_data)
# TypeError: create_user() got an unexpected keyword argument 'name'20.5.3) Menggabungkan Unpacking dengan Argumen Biasa
Kamu bisa mencampur argumen yang dibongkar dengan argumen biasa:
def calculate_total(base_price, tax_rate, discount):
"""Calculate total price after tax and discount."""
subtotal = base_price * (1 + tax_rate)
total = subtotal * (1 - discount)
return round(total, 2)
# Sebagian argumen biasa, sebagian dibongkar
pricing = [0.08, 0.10] # tax_rate dan discount
total = calculate_total(100, *pricing)
print(total)Output:
97.2Kamu juga bisa membongkar beberapa koleksi dalam satu pemanggilan:
def create_full_address(street, city, state, zip_code, country):
"""Create a complete address."""
return f"{street}, {city}, {state} {zip_code}, {country}"
street_address = ["123 Main St", "Springfield"]
location_details = ["IL", "62701", "USA"]
address = create_full_address(*street_address, *location_details)
print(address)Output:
123 Main St, Springfield, IL 62701, USA20.5.4) Contoh Praktis: Pemanggilan Fungsi yang Fleksibel
Unpacking sangat berguna saat bekerja dengan data dari sumber eksternal:
def send_email(recipient, subject, body, cc=None, bcc=None):
"""Send an email with optional CC and BCC."""
message = f"To: {recipient}\nSubject: {subject}\n\n{body}"
if cc:
message += f"\nCC: {cc}"
if bcc:
message += f"\nBCC: {bcc}"
return message
# Data email dari file konfigurasi atau database
email_config = {
"recipient": "user@example.com",
"subject": "Welcome",
"body": "Thank you for signing up!",
"cc": "manager@example.com"
}
# Bongkar konfigurasi secara langsung
result = send_email(**email_config)
print(result)Output:
To: user@example.com
Subject: Welcome
Thank you for signing up!
CC: manager@example.comPola ini memudahkan untuk mengoper argumen fungsi sebagai struktur data, yang umum saat membangun API atau memproses file konfigurasi.
20.6) Jebakan Argumen Default yang Mutable (Kenapa Default List Bertahan)
Salah satu jebakan Python yang paling terkenal melibatkan penggunaan objek mutable (seperti list atau dictionary) sebagai nilai default parameter. Memahami masalah ini sangat penting untuk menulis fungsi yang benar.
20.6.1) Masalahnya: Default Mutable yang Dibagikan
Pertimbangkan fungsi yang tampak tidak berbahaya ini:
def add_student(name, grades=[]):
"""Add a student with their grades."""
grades.append(name)
return grades
# Pemanggilan pertama
students1 = add_student("Alice")
print(students1)
# Pemanggilan kedua - berharap list baru
students2 = add_student("Bob")
print(students2)
# Pemanggilan ketiga
students3 = add_student("Carol")
print(students3)Output:
['Alice']
['Alice', 'Bob']
['Alice', 'Bob', 'Carol']Perilaku ini mengejutkan banyak programmer. Setiap pemanggilan add_student() tanpa memberikan argumen grades menggunakan objek list yang sama, bukan yang baru. List tersebut bertahan antar pemanggilan fungsi, terus menumpuk nilai.
20.6.2) Kenapa Ini Terjadi: Nilai Default Dibuat Sekali
Kunci untuk memahami perilaku ini adalah mengetahui kapan nilai default dibuat. Python mengevaluasi nilai default parameter sekali, saat fungsi didefinisikan, bukan setiap kali fungsi dipanggil.
def demonstrate_default_creation():
"""Show when defaults are created."""
print("Function defined!")
def use_default(value=demonstrate_default_creation()):
"""Use a default that calls a function."""
return value
# Pesan dicetak saat fungsi DIDEFINISIKAN, bukan saat dipanggilOutput:
Function defined!Saat Python menemui baris def use_default, Python mengevaluasi parameter default value=demonstrate_default_creation(). Ini memanggil demonstrate_default_creation(), yang langsung mencetak "Function defined!". Pemanggilan use_default() setelah itu tidak mengevaluasi default lagi, jadi tidak ada yang tercetak tambahan.
Saat Python menemui def add_student(name, grades=[]):, Python membuat sebuah objek list kosong dan menyimpannya sebagai nilai default untuk grades. Setiap pemanggilan berikutnya yang tidak menyediakan argumen grades akan memakai objek list yang sama.
Berikut demonstrasi yang lebih jelas menggunakan identitas objek:
def show_list_identity(items=[]):
"""Show that the same list object is reused."""
print(f"List ID: {id(items)}")
items.append("item")
return items
# Setiap pemanggilan memakai objek list yang sama (ID yang sama)
show_list_identity()
show_list_identity()
show_list_identity()Output:
List ID: 140234567890123
List ID: 140234567890123
List ID: 140234567890123Angka ID yang tepat akan bervariasi di sistem kamu, tetapi perhatikan bahwa ketiga pemanggilan menunjukkan nilai ID yang sama, membuktikan bahwa mereka memakai objek list yang sama. Fungsi id() mengembalikan pengenal unik untuk setiap objek di memori—kalau ID-nya sama, itu objek yang sama.
20.6.3) Pola yang Benar: Gunakan None sebagai Default
Solusi standar adalah menggunakan None sebagai default dan membuat objek mutable baru di dalam fungsi:
def add_student_correct(name, grades=None):
"""Add a student with their grades (correct version)."""
if grades is None:
grades = [] # Buat list BARU setiap kali
grades.append(name)
return grades
# Sekarang setiap pemanggilan mendapat list masing-masing
students1 = add_student_correct("Alice")
print(students1)
students2 = add_student_correct("Bob")
print(students2)
students3 = add_student_correct("Carol")
print(students3)Output:
['Alice']
['Bob']
['Carol']Pola ini bekerja karena None bersifat immutable dan list baru dibuat di dalam body fungsi setiap kali grades adalah None.
20.6.4) Masalah yang Sama pada Dictionary
Masalah ini memengaruhi semua tipe mutable, bukan hanya list:
# SALAH: default dictionary
def create_config_wrong(key, value, config={}):
"""Create a configuration (BUGGY VERSION)."""
config[key] = value
return config
config1 = create_config_wrong("theme", "dark")
print(config1)
config2 = create_config_wrong("language", "en")
print(config2)
print("---")
# BENAR: None sebagai default
def create_config_correct(key, value, config=None):
"""Create a configuration (CORRECT VERSION)."""
if config is None:
config = {}
config[key] = value
return config
config1 = create_config_correct("theme", "dark")
print(config1)
config2 = create_config_correct("language", "en")
print(config2)Output:
{'theme': 'dark'}
{'theme': 'dark', 'language': 'en'}
---
{'theme': 'dark'}
{'language': 'en'}20.6.5) Ringkasan: Aturan Emas
Jangan pernah memakai objek mutable (list, dictionary, set) sebagai nilai default parameter. Selalu gunakan None dan buat objek mutable di dalam fungsi:
# ❌ SALAH
def function(items=[]):
pass
# ✅ BENAR
def function(items=None):
if items is None:
items = []
# Sekarang gunakan items dengan amanPola ini memastikan setiap pemanggilan fungsi mendapatkan objek mutable independen miliknya sendiri, mencegah bug misterius saat data “bocor” antar pemanggilan.
Dalam bab ini, kita sudah mengeksplorasi sistem parameter dan argumen Python yang fleksibel secara mendalam. Kamu sudah belajar cara menggunakan argumen positional dan keyword, menyediakan nilai default, menangani jumlah argumen yang bervariasi dengan *args dan **kwargs, membongkar (unpack) koleksi saat memanggil fungsi, dan menghindari jebakan argumen default yang mutable.
Mekanisme ini memberi kamu alat yang kuat untuk merancang antarmuka fungsi yang fleksibel sekaligus mudah digunakan. Saat kamu menulis lebih banyak fungsi, kamu akan mengembangkan intuisi tentang pola parameter mana yang paling cocok untuk situasi yang berbeda. Kuncinya adalah menyeimbangkan fleksibilitas dengan kejelasan—buat fungsi kamu mudah dipanggil dengan benar dan sulit dipanggil dengan salah.