Để lưu trữ một hàm không liên kết đơn giản, chỉ cần đưa thẻ @memorise[] vào định nghĩa hàm [cần có dấu ngoặc đơn vì trình trang trí cần được khởi tạo tại thời điểm liên kết để xử lý các đối số cụ thể của ghi nhớ]
from memorised.decorators import memorise @memorise[] def myfunction[]: return 'hello world'
Bạn có thể làm tương tự đối với các phương thức lớp và thể hiện đơn giản, tuy nhiên đối với hầu hết các phương thức thể hiện, e. g. khi lưu kết quả vào bộ nhớ đệm cho các mô hình cơ sở dữ liệu, bạn có thể muốn bao gồm một số dạng nhận dạng để chọn ra một lệnh gọi phương thức trên một phiên bản từ một phiên bản khác. Bạn có thể làm điều này bằng cách cung cấp danh sách một hoặc nhiều khóa cha khác, đây là tên của các thuộc tính trong phiên bản cha mà bạn muốn được thêm vào khóa memcache
Đệ quy cung cấp cho các lập trình viên một cách thuận tiện để chia các vấn đề lớn hơn thành các phần có thể quản lý được. Xem xét các giải pháp lặp và đệ quy cho một tổng số fibonacci
# iterative
def fib_iterative[n]:
if [n == 0]:
return 0
elif [n == 1]:
return 1
elif [n >1 ]:
fn = 0
fn1 = 1
fn2 = 2
for i in range[3, n]:
fn = fn1+fn2
fn1 = fn2
fn2 = fn
return fn# recursive
def fib[n]:
if n == 0:return 0
if n == 1:return 1
else: return fib[n-1] + fib[n-2]
Các giải pháp đệ quy thường dễ đọc và dễ viết hơn cho các vấn đề phân nhánh. Duyệt cây, duyệt đồ thị và chuỗi toán học [thường] được xử lý trực quan hơn bằng cách sử dụng đệ quy
Mặc dù nó mang lại sự thuận tiện, chi phí thời gian tính toán của đệ quy đối với các vấn đề phân nhánh tăng theo cấp số nhân với các giá trị lớn hơn của n
Hãy xem ngăn xếp cuộc gọi cho fib[6]
Chúng tôi thực hiện gần gấp đôi số thao tác ở mỗi cấp độ kế tiếp của cây. Cho chúng ta độ phức tạp thời gian O[2^n]
Nếu chúng ta nhìn kỹ hơn vào cái cây của mình, bạn sẽ nhận thấy rằng chúng ta đang lặp lại công việc. fib[2] được tính năm lần, fib[3] được tính ba lần, v.v. Mặc dù đây không phải là vấn đề đối với các giá trị nhỏ của n, hãy tưởng tượng khối lượng công việc lặp đi lặp lại khi tính toán fib[1000]. Khi chúng tôi sửa lại giải pháp đệ quy của mình, hãy thử chạy cùng một vấn đề [giả sử là 20] cho cả hai phiên bản và nhận thấy sự khác biệt đáng chú ý trong thời gian hoàn thành
Chắc chắn có một cách tốt để ngăn chặn công việc lặp lại và giữ giải pháp tao nhã của chúng tôi?
ghi nhớ. Bạn có thể có Bánh của bạn và Ăn nó nữaVới tính năng ghi nhớ, chúng ta có thể “ghi nhớ” [nhớ, lưu trữ] kết quả của các bài toán mà chúng ta đã xử lý trước đó và trả về kết quả được lưu trữ thay vì tính toán lặp lại
Ví dụ: một khi fib[2] được tính toán, chúng ta có thể lưu trữ kết quả và sử dụng kết quả đó trong bốn lần gặp fib[2] tiếp theo
Điều này làm giảm thời gian chạy của chúng ta từ O[2^n] xuống O[n] vì chúng ta chỉ tính toán từng sợi [0]… sợi [n] một lần
Một cách để thực hiện điều này là sử dụng từ điển
__fib_cache = {}def fib_memo[n]:
if n in __fib_cache:
return __fib_cache[n]
else:
__fib_cache[n] = n if n < 2 else fib_memo[n-2] + fib_memo[n-1]
return __fib_cache[n]
Và Voila. Chúng tôi đã ghi nhớ thành công một hàm đệ quy
Tuy nhiên, chúng ta có thể tiến xa hơn một bước và ghi nhớ trừu tượng cho bất kỳ hàm đệ quy nào
người trang trí. Chức năng sử dụng chức năng để thực hiện chức năng khácDecorators được gọi là “Higher order functions”. các hàm lấy các hàm khác làm tham số. Ở đây chúng tôi sẽ sử dụng một trình trang trí để khái quát hóa việc ghi nhớ
Đây là một giải pháp trang trí
def memoize[func]:
cache = func.cache = {} @functools.wraps[func]
def memoized_func[*args, **kwargs]:
key = str[args] + str[kwargs]
if key not in cache:
cache[key] = func[*args, **kwargs]
return cache[key] return memoized_func@memoize
def fibonacci[n]:
if n == 0:return 0
if n == 1:return 1
else: return fib[n-1] + fib[n-2]
Hãy chia nhỏ nó xuống từng dòng
Trình trang trí của chúng tôi là một hàm nhận một hàm khác làm đối số, chúng tôi khai báo nó như vậy trên dòng 1
Tiếp theo, chúng tôi khai báo một từ điển func.cache = {}
, được khai báo là thuộc tính củafunc
. Ở đây chúng tôi sẽ lưu trữ từng cuộc gọi duy nhất được thực hiện bởi func.
Chúng tôi sẽ quay lại @functools.wraps
def memoized_func[*args, **kwargs]:
— Bên trong bộ trang trí, chúng tôi xác định một chức năng mới, được ghi nhớ dựa trên func
mà chúng tôi đã truyền vào và nó nhận các tham số tương tự là func
. Chúng tôi gọi đây là phiên bản mới
__fib_cache = {}def fib_memo[n]:0
if n in __fib_cache:
return __fib_cache[n]
else:
__fib_cache[n] = n if n < 2 else fib_memo[n-2] + fib_memo[n-1]
return __fib_cache[n]
__fib_cache = {}def fib_memo[n]:1 là một từ khóa chỉ ra rằng
if n in __fib_cache:
return __fib_cache[n]
else:
__fib_cache[n] = n if n < 2 else fib_memo[n-2] + fib_memo[n-1]
return __fib_cache[n]
__fib_cache = {}def fib_memo[n]:2 nhận một số đối số tùy ý e. g.
if n in __fib_cache:
return __fib_cache[n]
else:
__fib_cache[n] = n if n < 2 else fib_memo[n-2] + fib_memo[n-1]
return __fib_cache[n]
__fib_cache = {}def fib_memo[n]:3 hoặc
if n in __fib_cache:
return __fib_cache[n]
else:
__fib_cache[n] = n if n < 2 else fib_memo[n-2] + fib_memo[n-1]
return __fib_cache[n]
__fib_cache = {}def fib_memo[n]:4 đều hợp lệ. Tương tự,
if n in __fib_cache:
return __fib_cache[n]
else:
__fib_cache[n] = n if n < 2 else fib_memo[n-2] + fib_memo[n-1]
return __fib_cache[n]
__fib_cache = {}def fib_memo[n]:5 đại diện cho một số lượng đối số từ khóa tùy ý [các tham số được xác định tại lệnh gọi hàm] e. g.
if n in __fib_cache:
return __fib_cache[n]
else:
__fib_cache[n] = n if n < 2 else fib_memo[n-2] + fib_memo[n-1]
return __fib_cache[n]
__fib_cache = {}def fib_memo[n]:6. Ghi chú. Bạn có thể sử dụng các tham số chuyên dụng này trong bất kỳ chức năng nào
if n in __fib_cache:
return __fib_cache[n]
else:
__fib_cache[n] = n if n < 2 else fib_memo[n-2] + fib_memo[n-1]
return __fib_cache[n]
Dòng sau khai báo một
__fib_cache = {}def fib_memo[n]:7 cho
if n in __fib_cache:
return __fib_cache[n]
else:
__fib_cache[n] = n if n < 2 else fib_memo[n-2] + fib_memo[n-1]
return __fib_cache[n]
__fib_cache = {}def fib_memo[n]:8 của chúng ta dựa trên các tham số đã cho. Nếu chúng tôi đã thấy sự kết hợp các tham số này trước đây, chúng tôi sẽ lấy kết quả từ
if n in __fib_cache:
return __fib_cache[n]
else:
__fib_cache[n] = n if n < 2 else fib_memo[n-2] + fib_memo[n-1]
return __fib_cache[n]
__fib_cache = {}def fib_memo[n]:8 , nếu không, hãy tạo một khóa mới bằng cách sử dụng
if n in __fib_cache:
return __fib_cache[n]
else:
__fib_cache[n] = n if n < 2 else fib_memo[n-2] + fib_memo[n-1]
return __fib_cache[n]
func
ban đầu như đã được xác định [e. g. sử dụng def memoize[func]:1] và gán giá trị của nó cho kết quả e. g. [ lưu trữ kết quả của
cache = func.cache = {} @functools.wraps[func]
def memoized_func[*args, **kwargs]:
key = str[args] + str[kwargs]
if key not in cache:
cache[key] = func[*args, **kwargs]
return cache[key] return memoized_func@memoize
def fibonacci[n]:
if n == 0:return 0
if n == 1:return 1
else: return fib[n-1] + fib[n-2]
def memoize[func]:2]
cache = func.cache = {} @functools.wraps[func]
def memoized_func[*args, **kwargs]:
key = str[args] + str[kwargs]
if key not in cache:
cache[key] = func[*args, **kwargs]
return cache[key] return memoized_func@memoize
def fibonacci[n]:
if n == 0:return 0
if n == 1:return 1
else: return fib[n-1] + fib[n-2]
Dòng cuối cùng bên trong
def memoize[func]:3 trả về kết quả của một lần gọi tới
cache = func.cache = {} @functools.wraps[func]
def memoized_func[*args, **kwargs]:
key = str[args] + str[kwargs]
if key not in cache:
cache[key] = func[*args, **kwargs]
return cache[key] return memoized_func@memoize
def fibonacci[n]:
if n == 0:return 0
if n == 1:return 1
else: return fib[n-1] + fib[n-2]
func
với các tham số đã choDòng cuối cùng trả về
def memoize[func]:3, đại diện cho một phiên bản đã sửa đổi của hàm ban đầu của chúng tôi với các tính năng [được trang trí] bổ sung, nghĩa là lưu trữ kết quả của mỗi lệnh gọi mới
cache = func.cache = {} @functools.wraps[func]
def memoized_func[*args, **kwargs]:
key = str[args] + str[kwargs]
if key not in cache:
cache[key] = func[*args, **kwargs]
return cache[key] return memoized_func@memoize
def fibonacci[n]:
if n == 0:return 0
if n == 1:return 1
else: return fib[n-1] + fib[n-2]
Cuối cùng, chúng tôi đánh dấu chức năng ban đầu của mình bằng
def memoize[func]:6. Ký hiệu '@' yêu cầu trình biên dịch chuyển hàm ngay bên dưới nó thành một hàm bậc cao hơn có tên là
cache = func.cache = {} @functools.wraps[func]
def memoized_func[*args, **kwargs]:
key = str[args] + str[kwargs]
if key not in cache:
cache[key] = func[*args, **kwargs]
return cache[key] return memoized_func@memoize
def fibonacci[n]:
if n == 0:return 0
if n == 1:return 1
else: return fib[n-1] + fib[n-2]
__fib_cache = {}def fib_memo[n]:0 — trong trường hợp của chúng tôi, hàm này trả về một phiên bản được ghi nhớ của
if n in __fib_cache:
return __fib_cache[n]
else:
__fib_cache[n] = n if n < 2 else fib_memo[n-2] + fib_memo[n-1]
return __fib_cache[n]
def memoize[func]:8. Phiên bản này của hàm sẽ ngay lập tức trả về kết quả mà chúng ta đã tính toán trước đó, nếu không, nó sẽ gọi hàm
cache = func.cache = {} @functools.wraps[func]
def memoized_func[*args, **kwargs]:
key = str[args] + str[kwargs]
if key not in cache:
cache[key] = func[*args, **kwargs]
return cache[key] return memoized_func@memoize
def fibonacci[n]:
if n == 0:return 0
if n == 1:return 1
else: return fib[n-1] + fib[n-2]
def memoize[func]:9 ban đầu để xác định số đó sẽ là bao nhiêu@functools là gì. kết thúc tốt đẹp?
cache = func.cache = {} @functools.wraps[func]
def memoized_func[*args, **kwargs]:
key = str[args] + str[kwargs]
if key not in cache:
cache[key] = func[*args, **kwargs]
return cache[key] return memoized_func@memoize
def fibonacci[n]:
if n == 0:return 0
if n == 1:return 1
else: return fib[n-1] + fib[n-2]
@functools.wraps
là một trình trang trí khác được tích hợp sẵn trong python. Nó cho phép decoratorfunc.cache = {}
1 lưu trữ thông tin liên quan đến chuỗi tài liệu của chức năng đã ghi nhớ hoặc tên chức năng để có thể truy cập thông tin sau này. Nếu chúng tôi hỏi python func.cache = {}
2 hoặc func.cache = {}
3 [đây là các thuộc tính được tích hợp sẵn cho các hàm], mà không có @functools.wraps
, chúng tôi sẽ trả về tên và chuỗi tài liệu cho func.cache = {}
5 chứ không phải chính
def memoize[func]:1. Đối với mục đích gỡ lỗi, thật hữu ích khi có quyền truy cập vào tên và chuỗi tài liệu của chức năng ban đầu được gọi, không phải chức năng trang trí, vì cùng một công cụ trang trí có thể được áp dụng cho vô số chức năngHãy tự mình thử
cache = func.cache = {} @functools.wraps[func]
def memoized_func[*args, **kwargs]:
key = str[args] + str[kwargs]
if key not in cache:
cache[key] = func[*args, **kwargs]
return cache[key] return memoized_func@memoize
def fibonacci[n]:
if n == 0:return 0
if n == 1:return 1
else: return fib[n-1] + fib[n-2]
Lần tới khi bạn gặp một hàm đệ quy, hãy nghĩ xem liệu bạn có thể cải thiện hiệu suất của nó bằng tính năng ghi nhớ hay không, sau đó trừu tượng hóa tính năng ghi nhớ đó cho các hàm khác bằng cách thêm một trình trang trí