Python: Có phải là hình thức xấu để đưa ra ngoại lệ trong __init__ không?


128

Được coi là hình thức xấu để nâng cao ngoại lệ trong __init__? Nếu vậy, phương thức ném lỗi được chấp nhận là gì khi các biến lớp nhất định được khởi tạo là Nonehoặc không đúng loại?


Trong C, đó là hình thức xấu để đưa ra một ngoại lệ trong hàm hủy, nhưng hàm tạo sẽ ổn.
Calyth

6
@Calyth, ý bạn là C ++ - không có nhà xây dựng hay kẻ hủy diệt nào trong C.
Alex Martelli

4
... Hoặc ngoại lệ, cho vấn đề đó.
Matt Wishing

@Calyth: lol, vui lòng xóa câu nói vô nghĩa đó được tạo bởi một lỗi đánh máy vô tội ;-)
lpapp

4
@lpapp vâng. C ++. FML. Và tôi không thể chỉnh sửa bình luận đó. Vì vậy, internet sẽ ghi lại sự ngu ngốc của tôi;)
Calyth

Câu trả lời:


159

Tăng ngoại lệ trong __init__() là hoàn toàn tốt. Không có cách nào khác tốt để chỉ ra một điều kiện lỗi trong hàm tạo và có hàng trăm ví dụ trong thư viện chuẩn nơi xây dựng một đối tượng có thể đưa ra một ngoại lệ.

Lớp lỗi để nâng, tất nhiên, là tùy thuộc vào bạn. ValueErrorlà tốt nhất nếu hàm tạo được truyền tham số không hợp lệ.


14
Các ngoại lệ được sử dụng nhiều nhất trong constructor là ValueErrorTypeError.
Denis Otkidach

9
Chỉ cần chỉ ra rằng đó __init__không phải là nhà xây dựng, đó là intializer. Bạn có thể muốn chỉnh sửa dòng Không có cách nào khác tốt để chỉ ra tình trạng lỗi trong hàm tạo, ..
Haris

26

Đúng là cách thích hợp duy nhất để chỉ ra lỗi trong hàm tạo là đưa ra một ngoại lệ. Đó là lý do tại sao trong C ++ và trong các ngôn ngữ hướng đối tượng khác được thiết kế có tính an toàn ngoại lệ, hàm hủy sẽ không được gọi nếu một ngoại lệ được ném vào hàm tạo của một đối tượng (có nghĩa là việc khởi tạo đối tượng không đầy đủ). Đây thường không phải là trường hợp trong các ngôn ngữ kịch bản, chẳng hạn như Python. Ví dụ: đoạn mã sau ném AttributionError nếu socket.connect () không thành công:

class NetworkInterface:
    def __init__(self, address)
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.connect(address)
        self.stream = self.socket.makefile()

    def __del__(self)
        self.stream.close()
        self.socket.close()

Lý do là hàm hủy của đối tượng không hoàn chỉnh được gọi sau khi thử kết nối không thành công, trước khi thuộc tính luồng được khởi tạo. Bạn không nên tránh ném ngoại lệ từ các nhà xây dựng, tôi chỉ nói rằng thật khó để viết mã an toàn ngoại lệ hoàn toàn bằng Python. Một số nhà phát triển Python tránh sử dụng các hàm hủy hoàn toàn, nhưng đó là vấn đề của một cuộc tranh luận khác.


Câu trả lời này thực sự hữu ích. Nó không chỉ nói về "đèn xanh", mà còn đề cập đến một ví dụ với "kẻ hủy diệt".
lpapp

Mã python của bạn không thành công __del__vì nó được viết xấu. Bạn sẽ có chính xác vấn đề tương tự nếu bạn đã làm this->p_socket->close()trong một hàm hủy trong C ++. Trong C ++, bạn sẽ không làm điều đó - bạn sẽ để đối tượng thành viên tự hủy. Làm tương tự trong trăn.
Eric

1
@Eric Tôi sẽ không gặp vấn đề trong C ++ vì C ++ không phá hủy các đối tượng chưa được khởi tạo đúng cách . Trên thực tế, đó là một thành ngữ rất phổ biến trong C ++ để phân bổ tài nguyên trong hàm tạo và phân bổ chúng trong hàm hủy . Bạn đề nghị rằng đối tượng thành viên tự hủy hoại (giải phóng các tài nguyên trong hàm hủy của nó) - sau đó vấn đề tương tự phát sinh khi viết lớp thành viên.
Seppo Enarvi

11

Tôi không thấy bất kỳ lý do nào mà nó phải là hình thức xấu.

Ngược lại, một trong những điều ngoại lệ được biết là hoạt động tốt, trái ngược với việc trả lại mã lỗi, là mã lỗi thường không thể được trả về bởi các nhà xây dựng. Vì vậy, ít nhất là trong các ngôn ngữ như C ++, nâng cao ngoại lệ là cách duy nhất để báo hiệu lỗi.


6

Thư viện chuẩn cho biết:

>>> f = file("notexisting.txt")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'notexisting.txt'

Ngoài ra tôi không thực sự thấy bất kỳ lý do tại sao nó nên được coi là hình thức xấu.


3

Tôi nên nghĩ rằng đó là trường hợp hoàn hảo cho ValueErrorngoại lệ tích hợp.


2

Tôi đồng tình với tất cả những điều trên.

Thực sự không có cách nào khác để báo hiệu rằng có gì đó không ổn trong việc khởi tạo một đối tượng ngoài việc đưa ra một ngoại lệ.

Trong hầu hết các lớp chương trình trong đó trạng thái của một lớp hoàn toàn phụ thuộc vào các đầu vào của lớp đó, chúng ta có thể mong đợi một loại ValueError hoặc TypeError được nâng lên.

Các lớp có hiệu ứng phụ (ví dụ: lớp có kết nối mạng hoặc đồ họa) có thể gây ra lỗi trong init nếu (ví dụ) thiết bị mạng không khả dụng hoặc không thể ghi đối tượng canvas. Điều này nghe có vẻ hợp lý với tôi bởi vì bạn thường muốn biết về điều kiện thất bại càng sớm càng tốt.


2

Việc tăng lỗi từ init là không thể tránh khỏi trong một số trường hợp, nhưng thực hiện quá nhiều công việc trong init là một phong cách tồi. Bạn nên xem xét việc tạo ra một nhà máy hoặc một nhà máy giả - một phân loại đơn giản trả về đối tượng đã giải quyết.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.