Memoizing kwarg default dengan Python

Ini adalah bagaimana Anda dapat memo fungsi Python:

def memo_square(a, cache={}): if a not in cache: cache[a] = a*a return cache[a] 

Penerimaan tidak diketahui sedikit pun, jadi di bawah potongan, kami akan menganalisis cara kerjanya dan untuk apa.

Pertama, bagaimana dan mengapa itu berhasil. memo_square (seperti fungsi lainnya) adalah objek dari kelas fungsi, yang, di antara atribut lainnya, memiliki memo_square.__defaults__ tuple diisi saat membuat objek. Pertama, ini berisi kamus kosong, seperti yang ditunjukkan di header fungsi:

 >>> memo_square.__defaults__ ({},) 

__defaults__ adalah tuple biasa dan Anda tidak dapat mengubah elemen-elemennya. Benar, Anda dapat mengganti seluruh rangkaian nilai default sekaligus, tetapi hanya untuk tuple lain:

 >>> def test(a=1, b=2): ... print(a, b) ... >>> test.__defaults__ (1, 2) >>> test() 1 2 >>> test.__defaults__ = (', ', '') >>> test() ,  >>> test.__defaults__[1] = '' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment >>> test.__defaults__ = {0: ', ', 1: ''} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __defaults__ must be set to a tuple object 

Soryan, artikel ini tidak akan sampai ke Picaba. Baiklah, ini tidak penting. Yang penting adalah bahwa dengan pengecualian kode yang sangat rumit, func.__defaults__ dibuat sekali selama waktu program bersama dengan semua elemennya. Sebuah tuple dan elemen-elemennya tidak akan dibuat ulang dengan setiap pemanggilan fungsi, mereka akan digunakan selama fungsi itu ada. Tetapi untuk berubah, jika elemen-elemen itu sendiri bisa berubah, tidak ada yang melarangnya. Ketidakmampuan untuk bekerja dengan elemen-elemen tersebut adalah salah satu cara paling umum untuk menembak diri sendiri dengan python . Tetapi sebenarnya menyimpan nilai di antara panggilan fungsi bisa sangat berguna. Setelah beberapa panggilan, memo_square.__defaults__ akan terlihat seperti ini:

 >>> memo_square(2) 4 >>> memo_square.__defaults__ ({2: 4},) >>> memo_square(5) 25 >>> memo_square.__defaults__ ({2: 4, 5: 25},) >>> memo_square(2) 4 >>> memo_square.__defaults__ ({2: 4, 5: 25},) 

Jika fungsi telah dipanggil untuk nilai yang sama, maka nilainya dihitung dan, karenanya, cache tidak diisi ulang. Untuk kotak, manfaatnya kecil (secara tegas, untuk kotak, manfaatnya negatif, karena mencari di kamus lebih mahal daripada mengalikan dua angka), tetapi untuk fungsi yang sangat mahal, memoisasi / caching bisa berguna. Tentu saja, Anda bisa menyediakannya dengan python dalam lebih dari satu cara. Berikut adalah alternatif yang kami miliki:

  • @ functools.lru_cache . Dekorator dari modul functools yang mengingat panggilan fungsi terakhir. Ia dapat diandalkan dan sederhana, tetapi ia menggunakan semua parameter fungsi sebagai kunci, yang berarti ia membutuhkan hashability dan tidak dapat melihat bahwa dua nilai parameter yang berbeda secara formal adalah setara. Dengan persyaratan pertama, semuanya jelas, tentang fungsi dari set, misalnya, Anda bisa lupa. Baik, atau saat menelepon, konversikan ke frozenset. Adapun yang kedua, misalnya, saya memiliki fungsi yang menerima koneksi ke database SQL dan angka sebagai input, dan melakukan beberapa manipulasi dengan data yang terkait dengan nomor ini. Koneksi mungkin terputus dan dibangun kembali selama operasi program, dan cache lru_cache kemudian akan macet. Tapi dia tahu bagaimana cara cache hanya sejumlah panggilan (menghindari kebocoran memori) dan didokumentasikan dengan baik.
  • Fungsi Luar Cache:

     def square(a): return a**a cache = {} for x in values: if x not in cache: cache[x] = x**x print cache[x] 

    Artinya sama, tetapi jauh lebih rumit. Selain itu, variabel cache terlihat di luar fungsi, meskipun tidak digunakan untuk memo- sisnya. Cache selama memoisasi dengan argumen default hanya dapat diakses secara eksternal melalui func.__defaults__ , yang cukup sulit diakses secara tidak sengaja.
  • Siram objek penuh dengan cache dan buat fungsinya sebagai metode. Baik dalam hal arsitektur dan testabilitas, ini memungkinkan Anda untuk mempertahankan logika caching yang rumit, tetapi bahkan lebih rumit karena boilerplate dalam kode objek. Selain itu, tidak jelas apa yang diwarisi dari dan apakah akan diwarisi dari apa pun, jika ada lebih dari satu fungsi yang dapat di-memo.

Hal utama yang hilang dari metode memoisasi ini adalah tidak terlalu idiomatis. Secara pribadi, ketika saya menemukan keputusan ini untuk pertama kalinya, saya berpikir selama beberapa menit tentang apa yang terjadi di sini dan mengapa. Di sisi lain, dalam beberapa menit ini saya mulai memahami sedikit lebih baik bagaimana fungsi Python dan argumen mereka diatur. Jadi, bahkan jika Anda tidak menggunakan argumen default (untuk memoisasi atau, misalnya, mempercepat resolusi nama ), mengetahui teknik ini masih berguna untuk ahli gizi mana pun.

Source: https://habr.com/ru/post/id426341/


All Articles