Baru-baru ini saya telah melalui basis kode yang ada yang berisi banyak kelas di mana atribut contoh mencerminkan nilai yang disimpan dalam database. Saya telah merefaktor banyak atribut ini agar pencarian database mereka ditangguhkan, yaitu. tidak akan dijalankan di konstruktor tetapi hanya setelah dibaca pertama kali. Atribut ini tidak berubah selama masa pakai instance, tetapi merupakan hambatan nyata untuk menghitungnya pertama kali dan hanya benar-benar diakses untuk kasus khusus. Oleh karena itu, mereka juga dapat di-cache setelah diambil dari database (oleh karena itu hal ini sesuai dengan definisi memoisation di mana input hanyalah "tanpa input").
Saya mendapati diri saya mengetik cuplikan kode berikut berulang kali untuk berbagai atribut di berbagai kelas:
class testA(object):
def __init__(self):
self._a = None
self._b = None
@property
def a(self):
if self._a is None:
# Calculate the attribute now
self._a = 7
return self._a
@property
def b(self):
#etc
Apakah sudah ada dekorator untuk melakukan ini dengan Python yang tidak saya sadari? Atau, adakah cara yang cukup sederhana untuk mendefinisikan dekorator yang melakukan ini?
Saya bekerja dengan Python 2.5, tetapi jawaban 2.6 mungkin masih menarik jika berbeda secara signifikan.
Catatan
Pertanyaan ini ditanyakan sebelum Python memasukkan banyak dekorator siap pakai untuk ini. Saya telah memperbaruinya hanya untuk memperbaiki terminologi.
functools.lru_cache()
.Jawaban:
Untuk semua jenis utilitas hebat, saya menggunakan bolton .
Sebagai bagian dari perpustakaan itu, Anda memiliki properti cache :
sumber
Berikut ini contoh implementasi dekorator properti malas:
Sesi interaktif:
sumber
__get__
@wraps(fn)
bawah ini@property
untuk tidak kehilangan string dokumen Anda dll (wraps
berasal darifunctools
)Saya menulis ini untuk diri saya sendiri ... Untuk digunakan untuk properti malas yang dihitung satu kali . Saya menyukainya karena ini menghindari penempelan atribut tambahan pada objek, dan setelah diaktifkan tidak membuang waktu untuk memeriksa keberadaan atribut, dll .:
Catatan:
lazy_property
Kelas ini adalah deskriptor non-data , yang artinya ini hanya-baca. Menambahkan__set__
metode akan mencegahnya bekerja dengan benar.sumber
fget
jalan@property
tidak. Untuk memastikan kekekalan / idempotensi, Anda perlu menambahkan__set__()
metode yang memunculkanAttributeError('can\'t set attribute')
(atau pengecualian / pesan apa pun yang cocok untuk Anda, tetapi inilah yangproperty
muncul). Sayangnya ini datang dengan dampak kinerja sepersekian mikrodetik karena__get__()
akan dipanggil pada setiap akses daripada menarik nilai fget dari dict pada akses kedua dan selanjutnya. Layak menurut pendapat saya untuk mempertahankan kekekalan / idempotensi, yang merupakan kunci untuk kasus penggunaan saya, tetapi YMMV.Berikut adalah callable yang mengambil timeout argumen opsional, dalam
__call__
Anda juga bisa menyalin atas__name__
,__doc__
,__module__
dari namespace func ini:ex:
sumber
property
adalah sebuah kelas. Sebuah deskriptor tepatnya. Cukup dapatkan darinya dan terapkan perilaku yang diinginkan.sumber
Apa yang Anda benar-benar inginkan adalah
reify
(sumber terkait!) Dekorator dari Piramida:sumber
:)
pyramid
ketergantungan.Ada campuran istilah dan / atau kebingungan konsep baik dalam pertanyaan maupun jawaban sejauh ini.
Evaluasi malas hanya berarti bahwa sesuatu dievaluasi pada waktu proses pada saat-saat terakhir yang memungkinkan ketika suatu nilai diperlukan.
Dekoratorstandar(*) Fungsi yang didekorasi hanya dievaluasi dan setiap kali Anda membutuhkan nilai properti itu. (lihat artikel wikipedia tentang evaluasi malas)@property
melakukan hal itu.(*) Sebenarnya evaluasi malas yang sebenarnya (bandingkan mis. Haskell) sangat sulit dicapai dengan python (dan menghasilkan kode yang jauh dari idiomatik).
Memoisasi adalah istilah yang tepat untuk apa yang tampaknya dicari oleh penanya. Fungsi murni yang tidak bergantung pada efek samping untuk evaluasi nilai pengembalian dapat disimpan dengan aman dan sebenarnya terdapat dekorator di functools
@functools.lru_cache
sehingga tidak perlu menulis dekorator sendiri kecuali jika Anda memerlukan perilaku khusus.sumber
@property
, "malas" tidak masuk akal pada saat itu. (Saya juga menganggap memoisation sebagai peta input ke output yang di-cache, dan karena properti ini hanya memiliki satu input, tidak ada apa-apa, peta tampak lebih kompleks daripada yang diperlukan.)Anda dapat melakukan ini dengan baik dan mudah dengan membangun kelas dari properti asli Python:
Kita dapat menggunakan kelas properti ini seperti properti kelas biasa (Ini juga mendukung penugasan item seperti yang Anda lihat)
Nilai hanya dihitung pertama kali dan setelah itu kami menggunakan nilai simpanan kami
Keluaran:
sumber