2012-01-18 42 views
6

Tôi có một lớp mở tệp để ghi. Trong destructor của tôi, tôi gọi hàm đó đóng file:del MyClass không gọi đối tượng .__ del __()

class MyClass: 
    def __del__(self): 
     self.close() 

    def close(self): 
     if self.__fileHandle__ is not None: 
       self.__fileHandle__.close() 

nhưng khi tôi xóa các đối tượng với các mã như:

myobj = MyClass() 
myobj.open() 
del myobj 

nếu tôi cố gắng reinstantiate đối tượng, tôi nhận được một lỗi giá trị :

ValueError: The file 'filename' is already opened. Please close it before reopening in write mode. 

trong khi nếu tôi gọi myobj.close() trước del myobj Tôi không gặp sự cố này. Vậy tại sao không phải là __del__() được gọi?

+1

Hãy thử kế thừa từ 'đối tượng' - không làm như vậy đã được coi là phong cách lỗi thời trong khoảng sáu năm. – Marcin

+0

Bạn nên hiển thị thêm một số mã. Làm thế nào có thể cho bạn biết làm thế nào 'myobj.close()' thay đổi mọi thứ, mà không biết nó làm gì? – ugoren

+1

Hãy nhớ rằng 'del' không gọi' __del__'. Nó chỉ xóa một trong các tham chiếu. Nói chung tốt hơn là đóng một cách rõ ràng, có thể sử dụng giao thức quản lý ngữ cảnh (câu lệnh 'with'). – yak

Trả lời

8

Bạn có chắc chắn muốn sử dụng __del__ không? Có issues với __del__ và thu gom rác thải.

Bạn có thể làm MyClass một context manager thay vì:

class MyClass(object): 
    def __enter__(self): 
     return self 
    def __exit__(self,ext_type,exc_value,traceback): 
     if self.__fileHandle__ is not None: 
       self.__fileHandle__.close() 

Bằng cách đó, bạn có thể sử dụng MyClass như thế này:

with MyClass() as myobj: 
    ... 

myobj.__exit__ (và do đó self.__fileHandle__.close()) sẽ được gọi khi Python lá số with-block.

+0

+1 để đề cập đến vấn đề. http://docs.python.org/library/gc.html#gc.garbage nói rằng «Đối tượng có phương thức __del __() và là một phần của chu trình tham chiếu khiến toàn bộ chu trình tham chiếu không thể thu thập được, bao gồm các đối tượng không nhất thiết phải trong chu kỳ nhưng chỉ có thể truy cập được từ nó. » –

+0

Tôi không thấy làm thế nào có một chu trình tham chiếu trong ví dụ của OP. Có một đối tượng, nó nhận 'del''d, và' __del __() 'không được gọi. – sudo

+1

Ngoài ra, trong trường hợp của tôi, tôi thà để GC xử lý các công cụ vì nó không cần phải tất cả đóng cửa ngay lập tức, chỉ cuối cùng ... nhưng nó luôn luôn có vấn đề. Và không, các nhà quản lý ngữ cảnh không phải là một giải pháp thực sự. Chúng hạn chế bạn đóng đối tượng trong cùng một hàm gọi và thụt lề mã của bạn một lần mỗi khi bạn sử dụng nó. Làm cho nó không thể đọc được khi tôi muốn tạo ra 20 trong số này. – sudo

8

Đó không phải là những gì del thực hiện. Thật không may là __del__ có cùng tên với del, vì chúng không liên quan đến nhau. Trong thuật ngữ hiện đại, phương pháp __del__ sẽ được gọi là finalizer, không phải là số destructor và sự khác biệt là quan trọng.

Sự khác biệt ngắn là dễ bảo đảm khi một destructor được gọi, nhưng bạn có rất ít đảm bảo về thời điểm __del__ sẽ được gọi và có thể không bao giờ được gọi là. Có nhiều hoàn cảnh khác nhau có thể gây ra điều này.

Nếu bạn muốn phạm vi từ vựng, hãy sử dụng câu lệnh with. Nếu không, hãy gọi trực tiếp myobj.close(). Câu lệnh del chỉ xóa các tham chiếu, chứ không xóa các đối tượng.

Tôi tìm thấy một câu trả lời khác (link) cho một câu hỏi khác mà trả lời chi tiết hơn. Thật không may là câu trả lời được chấp nhận cho câu hỏi đó có chứa các lỗi nghiêm trọng.

Chỉnh sửa: Khi người nhận xét lưu ý, bạn cần kế thừa từ object. Điều đó là tốt, nhưng vẫn có thể là __del__ sẽ không bao giờ được gọi (bạn có thể nhận được may mắn). Xem câu trả lời được liên kết ở trên.

2

Mã của bạn nên được kế thừa từ object - không làm như vậy đã bị coi là lỗi thời (trừ trường hợp đặc biệt) trong ít nhất sáu năm.

Bạn có thể đọc về __del__ đây: http://docs.python.org/reference/datamodel.html#object.del

Các phiên bản ngắn của lý do tại sao bạn cần phải kế thừa từ object__del__ chỉ "ma thuật" trên lớp học kiểu mới.

Nếu bạn cần dựa vào việc gọi trình hoàn thiện, tôi khuyên bạn nên sử dụng phương pháp quản lý ngữ cảnh được đề xuất trong các câu trả lời khác, vì đó là giải pháp di động, mạnh mẽ.

+1

Ah chỉ tìm thấy một trường hợp mà "ma thuật" này không hoạt động ... bằng cách sử dụng ipython thay thế. – tdc

+0

@tdc: Trừ khi tôi bị nhầm lẫn, ipython dựa trên cpython. Trong mọi trường hợp, nếu bạn dựa vào hành vi của một cá thể cụ thể, mã của bạn sẽ không thể di chuyển được. Có lẽ sẽ tốt hơn nếu sử dụng phương pháp quản lý ngữ cảnh được đề xuất trong các câu trả lời khác. – Marcin

0

Có lẽ một cái gì đó khác là tham chiếu nó, đó là lý do tại sao __del__ không được gọi (chưa).

xem xét mã này:

#!/usr/bin/env python 

import time 

class NiceClass(): 
    def __init__(self, num): 
     print "Make %i" % num 
     self.num = num 
    def __del__(self): 
     print "Unmake %i" % self.num 

x = NiceClass(1) 
y = NiceClass(2) 
z = NiceClass(3) 
lst = [x, y, z] 
time.sleep(1) 
del x 
del y 
del z 
time.sleep(1) 
print "Deleting the list." 
del lst 
time.sleep(1) 

Nó không gọi __del__ của NiceClass trường hợp cho đến khi chúng tôi xóa danh sách tham chiếu đến chúng.

Không giống như C++, __del__ không được gọi là vô điều kiện để hủy đối tượng theo yêu cầu. GC làm cho mọi việc trở nên khó khăn hơn một chút. Dưới đây là một số thông tin: http://arctrix.com/nas/python/gc/

+0

Chắc chắn chỉ có một tham chiếu trong trường hợp này – tdc

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