Tuple yang kita pelajari pada Chapter Tuple adalah record yang sangat hemat, tetapi juga sangat diam: pesanan[1] tidak memberi petunjuk apa pun tentang isinya, dan pesanan[0] yang tertukar dengan pesanan[2] tetap sah secara tipe. namedtuple mempertahankan seluruh perilaku tuple — immutable, ringan, bisa di-unpack — sambil memberi setiap posisi sebuah nama. Kita akan mempelajari dua cara pembuatannya dan kapan record ringan ini lebih tepat daripada dataclass.

Record Bernama dengan collections.namedtuple #

Fungsi namedtuple() dari module collections adalah factory yang membuat class tuple baru. Interpreter Python membangun class tersebut sekali saat panggilan namedtuple() dieksekusi; setelah itu class bisa dipakai seperti class biasa.

python
from collections import namedtuple

Titik = namedtuple("Titik", ["x", "y"])

p = Titik(3, 7)

print(p.x, p.y)      # akses bernama
print(p[0], p[1])    # akses berindeks, seperti tuple biasa
print(p)             # repr yang informatif

x, y = p             # unpacking
print(f"x={x}, y={y}")

Output yang Dihasilkan:

text
3 7
3 7
Titik(x=3, y=7)
x=3, y=7

Mari kita telaah baris-baris pentingnya:

  1. Baris 3 (Titik = namedtuple("Titik", ["x", "y"])): membuat class Titik baru dengan dua field bernama x dan y. Argumen pertama adalah nama tipe untuk representasi, argumen kedua daftar nama field.
  2. Baris 5 (p = Titik(3, 7)): konstruktor class Titik menerima nilai sesuai urutan field, sama seperti tuple biasa.
  3. Baris 7 (p.x): akses bernama — p.x dan p[0] membaca nilai yang sama, jadi kode pembaca tidak lagi perlu menghafal posisi.
  4. Baris 11 (x, y = p): karena p adalah tuple, unpacking ke variabel x dan y bekerja langsung tanpa konversi.

_fields, _replace, dan _asdict #

Tiga method bawaan namedtuple paling sering dipakai untuk bekerja dengan record immutable:

python
from collections import namedtuple

Pesanan = namedtuple("Pesanan", ["id_pesanan", "pelanggan", "status"])

order = Pesanan("ORD-001", "Budi", "baru")

print(order._fields)
print(order._asdict())

# _replace() mengembalikan objek BARU, tidak memutasi yang lama
order_dikirim = order._replace(status="dikirim")
print(order)
print(order_dikirim)

# namedtuple immutable: assignment ditolak
try:
    order.status = "batal"
except AttributeError as e:
    print("AttributeError:", e)

Output yang Dihasilkan:

text
('id_pesanan', 'pelanggan', 'status')
{'id_pesanan': 'ORD-001', 'pelanggan': 'Budi', 'status': 'baru'}
Pesanan(id_pesanan='ORD-001', pelanggan='Budi', status='baru')
Pesanan(id_pesanan='ORD-001', pelanggan='Budi', status='dikirim')
AttributeError: can't set attribute

_fields mengembalikan tuple nama field, _asdict() mengubah record menjadi dict, dan _replace() membangun objek baru dengan sebagian field diganti — satu-satunya cara “mengubah” data yang immutable. Penugasan langsung order.status = "batal" ditolak dengan AttributeError, perilaku standar objek immutable sebagaimana dibahas pada Chapter Mutable dan Immutable Objects.

Dua method pendamping yang juga patut dikenal: Pesanan._make(["ORD-002", "Siti", "baru"]) membangun record dari iterable apa pun, dan Pesanan._asdict() hasilnya siap diserahkan ke json.dumps() karena berupa dict biasa. Karena namedtuple tidak menyimpan __dict__ per instance, memori yang dibutuhkan object-nya juga sama murahnya dengan tuple biasa — alasan utama record jenis ini populer pada pipeline yang memproses jutaan baris data.

Sintaks Class dengan typing.NamedTuple #

Untuk record yang sedikit lebih besar, module typing menyediakan NamedTuple dengan sintaks class dan annotasi tipe — kakek-neneknya @dataclass:

python
from typing import NamedTuple


class Pelanggan(NamedTuple):
    nama: str
    email: str = "[email protected]"

    def sapa(self) -> str:
        return f"Halo, {self.nama}!"


siti = Pelanggan("Siti")
budi = Pelanggan("Budi", "[email protected]")

print(siti)
print(budi.email)
print(budi.sapa())
print(siti._fields)

Output yang Dihasilkan:

text
Pelanggan(nama='Siti', email='[email protected]')
[email protected]
Halo, Budi!
('nama', 'email')

Perhatikan bahwa class Pelanggan mendukung nilai default per field (email), method biasa (sapa), dan tetap menghasilkan tuple — siti._fields serta unpacking bekerja persis seperti versi collections.namedtuple. Annotasi tipe pada class body sekaligus berfungsi sebagai deklarasi field, konvensi yang sama dengan Chapter Dataclasses.

namedtuple atau dataclass #

Keduanya sama-sama memecahkan masalah boilerplate; pembedanya ada pada sifat dasar hasilnya:

Aspeknamedtuple@dataclass
MutabilitasImmutable, selaluMutable secara default
Memori per instancePaling ringan (adalah tuple)Lebih besar, punya __dict__
Akses indeks, unpacking, _replaceYaTidak
Method, default factory, frozen/slots/orderTerbatasLengkap
Cocok untukrecord kecil, kembalian multi-nilai fungsimodel data utama aplikasi

Aturan praktisnya: jika data tidak pernah berubah setelah dibuat, ukurannya kecil, dan ingin bekerja dengan ekosistem tuple (unpacking, iterasi), pilih namedtuple. Jika data butuh mutasi, default factory, atau dibekukan dengan hash — fasilitas yang dibahas pada Chapter Dataclasses — dataclass memberi ruang lebih luas.