2009-09-26 52 views
41

Tôi đang đọc một mã. Có một lớp trong đó phương thức __del__ được xác định. Tôi đã tìm ra rằng phương pháp này được sử dụng để phá hủy một thể hiện của lớp. Tuy nhiên, tôi không thể tìm thấy một nơi mà phương pháp này được sử dụng. Lý do chính cho điều đó là tôi không biết làm thế nào phương pháp này được sử dụng, có lẽ không phải như thế: obj1.del(). Vì vậy, câu hỏi của tôi là làm thế nào để gọi phương thức __del__? Cảm ơn bạn đã giúp đỡ.Phương pháp __del__ là gì, Cách gọi nó là gì?

Trả lời

76

__del__ là một destructor. Nó được gọi khi một đối tượng là thu gom rác xảy ra sau khi tất cả các tham chiếu đến đối tượng đã bị xóa.

Trong một trường hợp đơn giản điều này có thể đúng sau khi bạn nói del x hoặc, nếu x là biến cục bộ, sau khi hàm kết thúc. Đặc biệt, trừ khi có các tham chiếu vòng tròn, CPython (việc triển khai Python chuẩn) sẽ thu thập rác ngay lập tức.

Tuy nhiên, đây là chi tiết triển khai của CPython. Các chỉ cần tài sản của thu gom rác thải Python là nó sẽ xảy ra sau khi tất cả các tài liệu tham khảo đã bị xóa, vì vậy điều này có thể không cần thiết xảy ra ngay sau khi có thể không xảy ra ở tất cả các.

Thậm chí nhiều hơn, các biến có thể tồn tại trong một thời gian dài cho nhiều lý do, ví dụ: một ngoại lệ lan truyền hoặc sự phân tích mô-đun có thể giữ số tham chiếu biến lớn hơn 0. Ngoài ra, biến có thể là một phần của chu kỳ tham chiếu - CPython với bộ sưu tập rác được bật nhiều nhất, nhưng không phải tất cả, chu kỳ như vậy, và thậm chí chỉ định kỳ .

Vì bạn đã có gì đảm bảo nó được thực hiện, người ta nên bao giờ đặt mã mà bạn cần phải được chạy vào __del__() - thay vào đó, mã này thuộc về finally khoản của khối try hoặc một người quản lý bối cảnh trong một tuyên bố with . Tuy nhiên, có trường hợp sử dụng hợp lệ cho __del__: ví dụ: nếu tham chiếu X đối tượng Y và cũng giữ bản sao tham chiếu Y trong toàn cầu cache (cache['X -> Y'] = Y) thì sẽ lịch sự để X.__del__ cũng xóa mục nhập bộ nhớ cache.

Nếu bạn biết rằng destructor cung cấp (trong vi phạm các quy định trên) một dọn dẹp cần thiết, bạn có thể muốn gọi nó trực tiếp, vì không có gì đặc biệt về nó như là một phương pháp là: x.__del__(). Rõ ràng, bạn nên làm như vậy chỉ khi bạn biết rằng nó không nhớ được gọi là hai lần. Hoặc, như một phương sách cuối cùng, bạn có thể xác định lại phương pháp này sử dụng

type(x).__del__ = my_safe_cleanup_method 
+4

Bạn nói rằng tính năng của CPython xóa đối tượng ngay lập tức sau khi số lượng tham chiếu của nó được giảm về 0 là "chi tiết triển khai". Tôi không tin. Bạn có thể cung cấp liên kết sao lưu xác nhận quyền sở hữu đó không? (Ý tôi là, font chữ đậm * là * khá thuyết phục về chính nó, nhưng các liên kết là một thứ hai gần ...:-) –

+8

** Chi tiết triển khai CPython: ** CPython hiện đang sử dụng lược đồ đếm tham chiếu với (tùy chọn) phát hiện trễ rác thải được liên kết theo chu kỳ, ... Các triển khai khác hoạt động khác và CPython có thể thay đổi. (http://docs.python.org/2/reference/datamodel.html) –

+0

Whats với '__exit__' trong ngữ cảnh này? Nó có chạy sau hoặc trước '__del__' hoặc cùng nhau không? – lony

6

Các __del__ phương pháp (chú ý chính tả!) Được gọi khi đối tượng của bạn cuối cùng cũng bị phá hủy. Về mặt kỹ thuật nói (trong cPython) đó là khi không có thêm tài liệu tham khảo cho đối tượng của bạn, tức là khi nó đi ra khỏi phạm vi.

Nếu bạn muốn xóa đối tượng của bạn và do đó gọi việc sử dụng phương pháp __del__

del obj1 

mà sẽ xóa các đối tượng (với điều kiện là không có bất kỳ tài liệu tham khảo khác để nó).

Tôi đề nghị bạn viết một lớp học nhỏ như thế này

class T: 
    def __del__(self): 
     print "deleted" 

Và điều tra trong thông dịch viên trăn, ví dụ:

>>> a = T() 
>>> del a 
deleted 
>>> a = T() 
>>> b = a 
>>> del b 
>>> del a 
deleted 
>>> def fn(): 
...  a = T() 
...  print "exiting fn" 
... 
>>> fn() 
exiting fn 
deleted 
>>> 

Lưu ý rằng Jython và IronPython có những quy định khác nhau như chính xác khi đối tượng là đã xóa và __del__ được gọi. Nó không được coi là thực hành tốt để sử dụng __del__ mặc dù vì điều này và thực tế là các đối tượng và môi trường của nó có thể ở trong một trạng thái không rõ khi nó được gọi. Nó không phải là hoàn toàn đảm bảo __del__ sẽ được gọi là một trong hai - thông dịch viên có thể thoát theo nhiều cách khác nhau mà không xóa tất cả các đối tượng.

+0

so với http://stackoverflow.com/a/2452895/611007 và http://stackoverflow.com/a/1481512/611007,' sử dụng del obj1' có vẻ như là một ý tưởng tồi dựa vào. – n611x007

4

Phương thức __del__, nó sẽ được gọi khi đối tượng được thu thập rác. Lưu ý rằng nó không nhất thiết được đảm bảo để được gọi là mặc dù. Mã sau đây sẽ không nhất thiết phải làm điều đó:

del obj 

Lý do là del chỉ giảm số lượng tham chiếu một. Nếu một cái gì đó khác có một tham chiếu đến đối tượng, __del__ sẽ không được gọi.

Mặc dù vậy, có một vài cách để sử dụng __del__. Nói chung, chúng thường không hữu ích lắm. Nghe có vẻ như tôi muốn sử dụng phương pháp gần hơn hoặc có thể là with statement.

Xem python documentation on __del__ methods.

Một điều khác cần lưu ý: __del__ phương pháp có thể ức chế thu gom rác nếu bị lạm dụng. Cụ thể, một tham chiếu vòng tròn có nhiều đối tượng với phương thức __del__ sẽ không bị thu gom rác. Điều này là do bộ thu gom rác không biết cái nào cần gọi trước. Xem tài liệu trên gc module để biết thêm thông tin.

39

Tôi đã viết câu trả lời cho một câu hỏi khác, mặc dù đây là câu hỏi chính xác hơn cho câu hỏi đó.

Can someone here explain constructors and destructors in python - simple explanation required - new to programming

Đây là câu trả lời hơi có ý kiến.

Không sử dụng __del__. Đây không phải là C++ hoặc một ngôn ngữ được xây dựng cho các destructors. Phương thức __del__ thực sự nên được sử dụng trong Python 3.x, mặc dù tôi chắc rằng ai đó sẽ tìm thấy một ca sử dụng hợp lý. Nếu bạn cần phải sử dụng __del__, nhận thức được những hạn chế cơ bản trên mỗi http://docs.python.org/reference/datamodel.html:

  • __del__ được gọi khi thu gom rác sẽ xảy ra là thu thập các đối tượng, không phải khi bạn bị mất tài liệu tham khảo cuối cùng đến một đối tượng và không phải khi bạn thực hiện del object.
  • __del__ chịu trách nhiệm gọi bất kỳ __del__ nào trong một lớp cha, mặc dù không rõ liệu đây có phải là thứ tự phân giải phương thức (MRO) hay chỉ gọi từng siêu lớp.
  • Có một __del__ có nghĩa là bộ thu gom rác sẽ từ bỏ việc phát hiện và làm sạch bất kỳ liên kết tuần hoàn nào, chẳng hạn như mất tham chiếu cuối cùng vào danh sách được liên kết. Bạn có thể nhận được một danh sách các đối tượng bị bỏ qua từ gc.garbage. Đôi khi bạn có thể sử dụng các tham chiếu yếu để tránh toàn bộ chu kỳ. Điều này được tranh luận ngay bây giờ và sau đó: xem http://mail.python.org/pipermail/python-ideas/2009-October/006194.html.
  • Chức năng __del__ có thể gian lận, lưu tham chiếu đến đối tượng và dừng thu gom rác.
  • Ngoại lệ được nêu rõ trong __del__ bị bỏ qua.
  • __del__ bổ sung __new__ nhiều hơn __init__. Điều này trở nên khó hiểu. Xem http://www.algorithm.co.il/blogs/programming/python-gotchas-1-del-is-not-the-opposite-of-init/ để được giải thích và nhận được.
  • __del__ không phải là con "được yêu thích" bằng Python. Bạn sẽ thấy rằng tài liệu sys.exit() không chỉ rõ nếu rác được thu thập trước khi thoát, và có rất nhiều vấn đề kỳ lạ. Gọi số __del__ trên hình cầu sẽ gây ra các vấn đề đặt hàng lẻ, ví dụ: http://bugs.python.org/issue5099. Nên __del__ gọi ngay cả khi __init__ không thành công? Xem http://mail.python.org/pipermail/python-dev/2000-March/thread.html#2423 để có chuỗi dài.

Nhưng, mặt khác:

Và lý do pesonal của tôi không thích chức năng __del__.

  • Mỗi khi ai đó trả lời __del__, nó sẽ biến thành ba mươi thư nhầm lẫn.
  • Nó phá vỡ các mục này trong Zen của Python:
    • Đơn giản là tốt hơn phức tạp.
    • Trường hợp đặc biệt không đủ đặc biệt để vi phạm quy tắc.
    • Lỗi không bao giờ được chuyển âm thầm.
    • Khi đối mặt với sự mơ hồ, từ chối sự cám dỗ để đoán.
    • Nên có một-- và tốt nhất là chỉ có một - cách rõ ràng để thực hiện điều đó.
    • Nếu việc thực hiện khó giải thích, đó là một ý tưởng tồi.

Vì vậy, hãy tìm một lý do để không sử dụng __del__.

+3

Ngay cả khi câu hỏi không chính xác: tại sao chúng ta không nên sử dụng '__del__', nhưng làm thế nào để gọi' __del__', câu trả lời của bạn là thú vị. – nbro

+0

Cảm ơn. Đôi khi ý tưởng tốt nhất là tránh xa những ý tưởng khủng khiếp. –

+0

Trong tin tức khác, tôi quên đề cập đến rằng PyPy (một trình thông dịch nhanh hơn cho các ứng dụng đang chạy lâu hơn) sẽ phá vỡ vào __del__. –

Các vấn đề liên quan