Tăng ngoại lệ so với trả về Không có trong các hàm?


85

Thực hành tốt hơn trong một hàm do người dùng xác định trong Python: raisemột ngoại lệ hoặc return None? Ví dụ: tôi có một chức năng tìm tệp gần đây nhất trong một thư mục.

def latestpdf(folder):
    # list the files and sort them
    try:
        latest = files[-1]
    except IndexError:
        # Folder is empty.
        return None  # One possibility
        raise FileNotFoundError()  # Alternative
    else:
        return somefunc(latest)  # In my case, somefunc parses the filename

Một tùy chọn khác là để lại ngoại lệ và xử lý nó trong mã người gọi, nhưng tôi cho rằng cách xử lý rõ ràng FileNotFoundErrorhơn là an IndexError. Hay việc nâng cấp lại một ngoại lệ bằng một tên khác có phải là hình thức xấu không?



3
Tôi nghiêng về việc nâng cao một ngoại lệ vì vậy tôi buộc phải xử lý ngoại lệ trong hàm gọi. Nếu tôi quên kiểm tra xem đầu ra có là Không trong chức năng gọi hay không, tôi có thể gặp lỗi tiềm ẩn. Nếu bạn trả về Không có, hy vọng dòng tiếp theo trong hàm gọi sẽ xuất hiện Lỗi thuộc tính. Tuy nhiên, nếu giá trị trả về được thêm vào từ điển và sau đó 100 hàm gọi trong một tệp nguồn khác thì một AttributeError được nâng lên, bạn sẽ rất vui khi tìm ra lý do tại sao giá trị đó là Không.
IceArdor

Nói chung, tôi cũng tránh các giá trị có ý nghĩa đặc biệt hoặc có nhiều chữ ký cho một hàm (nó có thể trả về một chuỗi hoặc Không có).
IceArdor

Câu trả lời:


91

Nó thực sự là một vấn đề về ngữ nghĩa. Nghĩa foo = latestpdf(d) gì?

Có hoàn toàn hợp lý khi không có tệp mới nhất không? Sau đó, chắc chắn, chỉ cần trả về Không.

Bạn đang mong đợi luôn tìm thấy một tệp mới nhất? Nâng cao một ngoại lệ. Và có, nâng cấp lại một ngoại lệ thích hợp hơn là được.

Nếu đây chỉ là một chức năng chung được áp dụng cho bất kỳ thư mục nào, tôi sẽ làm như cũ và trả về None. Nếu thư mục, ví dụ, có nghĩa là một thư mục dữ liệu cụ thể chứa tập hợp các tệp đã biết của một ứng dụng, tôi sẽ nêu ra một ngoại lệ.


3
Một điểm khác cần xem xét: Nếu tăng một ngoại lệ, một thông báo có thể được đính kèm, nhưng chúng ta không thể làm điều đó khi quay trở lại None.
kawing-chiu

9

Tôi sẽ đưa ra một vài gợi ý trước khi trả lời câu hỏi của bạn vì nó có thể trả lời câu hỏi cho bạn.

  • Luôn đặt tên mô tả các chức năng của bạn. latestpdfcó ý nghĩa rất nhỏ đối với bất kỳ ai nhưng nhìn qua chức năng của bạn latestpdf()sẽ nhận được bản pdf mới nhất. Tôi sẽ đề nghị bạn đặt tên cho nó getLatestPdfFromFolder(folder).

Ngay sau khi tôi làm điều này, nó đã trở nên rõ ràng những gì nó sẽ trả về .. Nếu không có bản pdf, hãy nêu ra một ngoại lệ. Nhưng hãy đợi ở đó thêm ..

  • Giữ các chức năng được xác định rõ ràng. Vì không rõ somefuc phải làm gì và không (rõ ràng) rõ ràng nó liên quan như thế nào đến việc tải bản pdf mới nhất, tôi khuyên bạn nên chuyển nó ra. Điều này làm cho mã dễ đọc hơn nhiều.

for folder in folders:
   try:
       latest = getLatestPdfFromFolder(folder)
       results = somefuc(latest)
   except IOError: pass

Hi vọng điêu nay co ich!


Hoặc get_latest_pdf_from_folder. Thật vậy, Pep8: "Tên hàm nên là chữ thường, với các từ được phân tách bằng dấu gạch dưới khi cần thiết để cải thiện khả năng đọc."
PatrickT

7

Tôi thường thích xử lý các ngoại lệ bên trong (tức là thử / ngoại trừ bên trong hàm được gọi, có thể trả về Không có) vì python được nhập động. Nói chung, tôi coi đó là một cuộc gọi phán đoán theo cách này hay cách khác, nhưng trong một ngôn ngữ được nhập động, có những yếu tố nhỏ có lợi cho việc không chuyển ngoại lệ cho người gọi:

  1. Bất kỳ ai gọi hàm của bạn đều không được thông báo về các ngoại lệ có thể được ném ra. Nó trở thành một hình thức nghệ thuật để biết bạn đang tìm kiếm loại ngoại lệ nào (và nên tránh những khối ngoại trừ chung chung).
  2. if val is Nonedễ dàng hơn một chút except ComplicatedCustomExceptionThatHadToBeImportedFromSomeNameSpace. Nghiêm túc mà nói, tôi ghét phải nhớ nhập from django.core.exceptions import ObjectDoesNotExistvào đầu tất cả các tệp django của mình chỉ để xử lý một trường hợp sử dụng thực sự phổ biến. Trong một thế giới được nhập tĩnh, hãy để trình chỉnh sửa làm điều đó cho bạn.

Thành thật mà nói, nó luôn luôn là một lệnh gọi phán đoán và tình huống bạn đang mô tả, trong đó hàm được gọi nhận lỗi mà nó không thể giúp được, là một lý do tuyệt vời để nêu lại một ngoại lệ có ý nghĩa. Bạn có ý tưởng chính xác đúng, nhưng trừ khi bạn ngoại lệ sẽ cung cấp nhiều thông tin có ý nghĩa hơn trong một dấu vết ngăn xếp hơn

AttributeError: 'NoneType' object has no attribute 'foo'

mà, chín lần trong số mười, là những gì người gọi sẽ thấy nếu bạn trả về một Không có gì không được xử lý, đừng bận tâm.

(Tất cả những điều này khiến tôi ước rằng các ngoại lệ python có các causethuộc tính theo mặc định, như trong java, cho phép bạn chuyển các ngoại lệ thành các ngoại lệ mới để bạn có thể nhập lại tất cả những gì bạn muốn và không bao giờ mất nguồn gốc của vấn đề.)


Đối số rằng các ngoại lệ có thể không được xác định và do đó rất khó để bắt được là đối số rất hợp lệ cho Python.
snorberhuis

4

với cách nhập của python 3.5 :

hàm ví dụ khi trả về None sẽ là:

def latestpdf(folder: str) -> Union[str, None]

và khi tăng một ngoại lệ sẽ là:

def latestpdf(folder: str) -> str 

tùy chọn 2 có vẻ dễ đọc hơn và dễ hiểu hơn

(+ tùy chọn để thêm nhận xét vào ngoại lệ như đã nêu trước đó.)


4
Union[str, None]nên đượcOptional[str]
Georgy

2
viết tắt, nhưng bạn nói đúng, nó dễ đọc hơn. không chỉnh sửa nên cả hai tùy chọn đều ở đây.
Asaf

2 có khả năng dễ đọc hơn nhưng (thật không may?) Các gợi ý loại không chỉ ra một ngoại lệ có thể được đưa ra. Gần đây, tôi nhận thấy rằng 1 sẽ giúp bắt được nhiều lỗi hơn vì bạn buộc phải xử lý trả lại Không có.
jonespm

2

Nói chung, tôi muốn nói rằng một ngoại lệ nên được ném nếu một cái gì đó thảm khốc đã xảy ra mà không thể khôi phục được (tức là hàm của bạn xử lý một số tài nguyên internet không thể kết nối với) và bạn nên trả về Không nếu hàm của bạn thực sự nên trả về một cái gì đó nhưng không có gì thích hợp để trả về (ví dụ: "Không có" nếu hàm của bạn cố gắng khớp một chuỗi con trong một chuỗi chẳng hạn).

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.