Idenya
Model rerun punya harga: setiap klik menjalankan seluruh script, termasuk bagian-bagian yang lambat. Kalau memuat datamu butuh 3 detik, setiap gerakan slider butuh 3 detik. Caching memperbaikinya: Streamlit mengingat hasil sebuah fungsi, dan saat fungsi itu dipanggil lagi dengan argumen yang sama, langsung mengembalikan hasil yang tersimpan alih-alih menjalankan fungsinya lagi.
Cache adalah catatan tempel di kulkas berisi jawaban kemarin. Sebelum kamu mengulang perhitungan yang panjang, kamu memeriksa apakah jawaban untuk pertanyaan yang persis sama sudah tertulis.
st.cache_data: untuk data
Memuat file CSV besar bisa memakan beberapa detik, dan karena seluruh script dijalankan ulang setiap kali widget berubah, file itu dimuat lagi di setiap klik. Solusinya adalah menaruh pemuatannya di sebuah fungsi lalu menandainya dengan @st.cache_data. Streamlit lalu mengingat hasilnya dan memberikannya kembali dengan instan lain kali:
import streamlit as st
import pandas as pd
@st.cache_data
def load_sales(path):
st.write("Loading data from disk...") # hanya terlihat saat fungsinya benar-benar berjalan
return pd.read_csv(path)
sales = load_sales("sales.csv")
st.write(f"{len(sales)} order lines loaded")
st.slider("Pick a number", 0, 10, 5)
Di eksekusi pertama kamu melihat "Loading data from disk...". Geser slider-nya: pesannya hilang, karena load_sales("sales.csv") dijawab dari cache. Isi fungsimu hanya berjalan saat cache tidak punya jawabannya.
st.cache_data untuk fungsi yang mengembalikan data: DataFrame, list, dict, angka, hasil pemanggilan API dan query database. Setiap pemanggilan mendapat salinan dari nilai yang di-cache, jadi kode yang mengubah DataFrame yang dikembalikan tidak bisa merusak cache-nya.
Cara kerja cache key
Bagaimana kalau jawaban fungsinya bergantung pada argumennya? Revenue per kategori dan revenue per channel adalah hasil yang berbeda. Cache menanganinya dengan menyimpan satu hasil per kombinasi argumen:
import streamlit as st
import pandas as pd
@st.cache_data
def revenue_by(column):
sales = pd.read_csv("sales.csv")
completed = sales[sales["status"] == "completed"]
return completed.groupby(column, as_index=False)["revenue"].sum()
choice = st.radio("Group by", ["category", "channel", "city"], horizontal=True)
st.dataframe(revenue_by(choice), hide_index=True)
Pertama kali setiap opsi dipilih, fungsinya berjalan; setelah itu, berpindah bolak-balik menjadi instan. Argumen adalah bagian dari key, jadi berikan semua hal yang memengaruhi hasilnya sebagai argumen. Fungsi yang diam-diam membaca variabel global atau nilai widget tidak akan sadar saat nilai itu berubah, dan akan mengembalikan hasil yang basi.
st.cache_resource: untuk koneksi dan model
Beberapa hal tidak boleh disalin: koneksi database, model machine learning, HTTP client. Semuanya mahal untuk dibuat dan dimaksudkan untuk dibagi:
import streamlit as st
import sqlite3
@st.cache_resource
def get_connection():
conn = sqlite3.connect(":memory:", check_same_thread=False)
conn.execute("CREATE TABLE visits (city TEXT)")
return conn
conn = get_connection()
st.write("Same connection object on every rerun:", id(conn))
st.cache_data | st.cache_resource | |
|---|---|---|
| Dipakai untuk | data: DataFrame, hasil query, respons API | resource: koneksi, model, client |
| Mengembalikan | salinan baru setiap pemanggilan | objek yang sama setiap pemanggilan |
| Dibagi antar pengguna | ya (masing-masing mendapat salinan) | ya (objek yang sama) |
Data yang akan kamu baca dan ubah → cache_data. Sesuatu yang akan kamu pakai (untuk terhubung, untuk memprediksi) → cache_resource.
Saat data yang di-cache menjadi usang
Hasil yang di-cache bertahan sampai aplikasi di-restart. Untuk data yang berubah (order hari ini), beri cache-nya umur:
import streamlit as st
import pandas as pd
@st.cache_data(ttl=600) # detik: refresh paling sering setiap 10 menit
def load_orders():
return pd.read_csv("sales.csv")
orders = load_orders()
if st.button("Refresh now"):
load_orders.clear() # lupakan hasil cache untuk fungsi ini
st.rerun()
st.write(f"{len(orders)} rows")
ttl=600(time to live): hasil yang lebih tua dari 10 menit dihitung ulang.load_orders.clear()mengosongkan cache fungsi ini;st.cache_data.clear()mengosongkan semua cache data.st.rerun()langsung memulai eksekusi baru, yang sekarang memuat data yang segar.
(Simulator di situs ini tidak membuat cache ttl kedaluwarsa berdasarkan waktu; parameternya diterima supaya kodemu berjalan tanpa perubahan di Streamlit sungguhan.)
Apa yang tidak perlu di-cache
- Fungsi yang sudah cepat: caching punya overhead dan menyembunyikan bug.
- Fungsi dengan efek samping yang kamu butuhkan setiap kali: mengirim email, menulis baris log. Cache hit melewati isi fungsinya, jadi efek sampingnya diam-diam tidak terjadi.
- Hasil yang bergantung pada sesuatu yang tidak ada di argumen (waktu saat ini, variabel global): hasilnya basi.
Mengapa ini penting untuk kode yang bersih
Caching menghargai kebiasaan fungsi bersih dari track Python: fungsi yang hasilnya hanya bergantung pada argumennya dan yang tidak punya efek samping persis yang bisa di-cache dengan benar. Kalau sebuah fungsi sulit di-cache, itu sering menjadi tanda bahwa ia mengerjakan dua tugas (memuat dan mencatat log, menghitung dan menampilkan). Pecah fungsinya, cache bagian yang murni.
Rangkuman
@st.cache_datameng-cache data yang dikembalikan per kombinasi argumen; pemanggil mendapat salinan.@st.cache_resourcemeng-cache objek yang dibagi (koneksi, model); pemanggil mendapat objek yang sama.- Berikan semua hal yang memengaruhi hasil sebagai argumen; hindari input tersembunyi.
ttl=membatasi umur;fn.clear()ataust.cache_data.clear()mengosongkan cache;st.rerun()menjalankan ulang sekarang.- Jangan cache fungsi yang cepat, efek samping, atau hasil dengan input tersembunyi.