RecursionError khi gọi hàm đã chọn [trùng lặp]
Tôi đang cố chạy mã sau:
import pickle
def foo():
print("i am foo")
pickle_foo = pickle.dumps(foo)
def foo():
print("i am the new foo")
fkt = pickle.loads(pickle_foo)
return fkt()
foo()
Hành vi mong đợi sẽ là:
- hàm được xác định mới "foo" được gọi là
- trong hàm mới, hàm cũ được bỏ chọn và sau đó được gọi là
đầu ra:
i am the new foo
i am foo
Điều thực sự xảy ra là:
- hàm mới foo được gọi, và sau đó gọi đệ quy chính nó cho đến khi xuất hiện Lỗi đệ quy:
RecursionError: maximum recursion depth exceeded while calling a Python object
Lỗi không xảy ra khi hai hàm được đặt tên khác nhau, nhưng điều đó sẽ rất không thực tế đối với dự án của tôi. Bất cứ ai có thể giải thích, tại sao hành vi này xảy ra và làm thế nào để tránh nó (mà không cần thay đổi tên hàm)?
Trả lời
Các picklechức năng dưa chua mô-đun dựa trên tài liệu tham khảo tên đầy đủ của họ. Điều này có nghĩa là nếu hàm của bạn được định nghĩa lại ở đâu đó trong mã, và sau đó bạn bỏ chọn một tham chiếu có sẵn cho nó, thì việc gọi hàm đó sẽ dẫn đến một lệnh gọi đến định nghĩa mới.
Từ tài liệu Python trên pickle:
Lưu ý rằng các chức năng (tích hợp sẵn và do người dùng xác định) được chọn bởi tham chiếu tên "đủ điều kiện", không phải theo giá trị. 2 Điều này có nghĩa là chỉ có tên hàm được chọn, cùng với tên của mô-đun mà hàm được xác định. Cả mã của hàm cũng như bất kỳ thuộc tính hàm nào của nó đều không được chọn. Do đó, mô-đun xác định phải có thể nhập được trong môi trường giải nén và mô-đun phải chứa đối tượng được đặt tên, nếu không sẽ xuất hiện ngoại lệ.
Tuy nhiên, những gì bạn có thể làm là sử dụng inspect.getsource()để truy xuất mã nguồn cho chức năng của bạn và xử lý điều đó. Điều này yêu cầu mã của bạn phải có sẵn dưới dạng nguồn ở đâu đó trên hệ thống tệp, vì vậy mã C đã biên dịch được nhập hoặc các nguồn bên ngoài khác (đầu vào trình thông dịch, mô-đun được tải động) sẽ không hoạt động.
Khi bạn bỏ chọn nó, bạn có thể sử dụng execđể chuyển đổi nó thành một hàm và thực thi nó.
Lưu ý: điều này sẽ xác định lại foomọi lúc, vì vậy các cuộc gọi đến fookhông thể được đảm bảo sẽ có cùng tác dụng.
Lưu ý 2: execkhông an toàn và thường không phù hợp với mã sẽ tương tác với các nguồn bên ngoài. Đảm bảo bạn bảo vệ các cuộc gọi đến execkhỏi các cuộc tấn công tiềm ẩn từ bên ngoài cố gắng thực thi mã tùy ý.