Python - khẳng định vs if & return


12

Tôi đang viết một kịch bản làm một cái gì đó cho một tệp văn bản (những gì nó làm không liên quan đến câu hỏi của tôi). Vì vậy, trước khi tôi làm gì đó với tệp tôi muốn kiểm tra xem tệp có tồn tại không. Tôi có thể làm điều này, không có vấn đề, nhưng vấn đề là nhiều hơn về thẩm mỹ.

Đây là mã của tôi, thực hiện cùng một thứ theo hai cách khác nhau.

def modify_file(filename):
    assert os.path.isfile(filename), 'file does NOT exist.'


Traceback (most recent call last):
  File "clean_files.py", line 15, in <module>
    print(clean_file('tes3t.txt'))
  File "clean_files.py", line 8, in clean_file
    assert os.path.isfile(filename), 'file does NOT exist.'
AssertionError: file does NOT exist.

hoặc là:

def modify_file(filename):
    if not os.path.isfile(filename):
        return 'file does NOT exist.'


file does NOT exist.

Phương thức đầu tiên tạo ra một đầu ra hầu như không đáng kể, điều duy nhất tôi quan tâm là tệp không tồn tại.

Phương thức thứ hai trả về một chuỗi, nó đơn giản.

Câu hỏi của tôi là: phương pháp nào tốt hơn để cho người dùng biết rằng tệp không tồn tại? Sử dụng assertphương pháp dường như bằng cách nào đó nhiều pythonic.

Câu trả lời:


33

Bạn muốn đi với một thứ ba tùy chọn thay vì: sử dụng raisevà một ngoại lệ cụ thể. Đây có thể là một trong những trường hợp ngoại lệ tích hợp hoặc bạn có thể tạo một ngoại lệ tùy chỉnh cho công việc.

Trong trường hợp này, tôi sẽ sử dụng IOError, nhưng ValueErrorcũng có thể phù hợp:

def modify_file(filename):
    if not os.path.isfile(filename):
        raise IOError('file does NOT exist.')

Sử dụng một ngoại lệ cụ thể cho phép bạn đưa ra các ngoại lệ khác cho các trường hợp ngoại lệ khác nhau và cho phép người gọi xử lý ngoại lệ một cách duyên dáng.

Tất nhiên, nhiều thao tác tệp (như open()) tự nâng lên OSError; rõ ràng thử nghiệm đầu tiên nếu tập tin tồn tại có thể là dư thừa ở đây.

Đừng sử dụng assert; nếu bạn chạy python với -Ocờ, tất cả các xác nhận sẽ bị xóa khỏi mã.


điểm thú vị về sự dư thừa! Bạn có đề nghị để tránh nó? tức là "dù sao thì nó cũng sẽ thất bại sau này"
Ciprian Tomoiagă

1
@ CiprianTomoiagă: tại sao tăng gấp đôi trong các bài kiểm tra? Nếu dòng tiếp theo modify_file()with open(filename) as f:, thì IOErrorcũng sẽ được nâng lên. Và các phiên bản Python gần đây đã cung cấp nhiều chi tiết hơn trong các lớp con IOError( FileNotFoundErrorcụ thể là trong tâm trí) có thể hữu ích cho nhà phát triển sử dụng API này. Nếu mã tự kiểm tra và tăng IOErrorthì chi tiết hữu ích đó sẽ bị mất.
Martijn Pieters

@MartijnPieters sẽ có một câu trả lời chấp nhận được cho "tại sao lại tăng gấp đôi trong các bài kiểm tra?" là khi kiểm tra đầu tiên nhanh hơn ngoại lệ được đưa ra khi open () không thành công? ví dụ: khi kiểm tra sự tồn tại của một tệp nhanh hơn so với cố gắng mở và cuối cùng không làm như vậy.
Marcel Wilson

1
@MarcelWilson: Không, vì bạn sẽ tối ưu hóa vi mô đối với phương pháp thực hiện I / O. Không có số lượng điều chỉnh trong ngữ nghĩa Python trong vài phút sẽ làm cho I / O đi nhanh hơn và chỉ làm tổn thương khả năng đọc và bảo trì. Tập trung vào các khu vực có nhiều tác động hơn, tôi nói.
Martijn Pieters

12

assertđược dành cho các trường hợp mà lập trình viên gọi hàm đã mắc lỗi, trái ngược với người dùng . Sử dụng asserttrong hoàn cảnh đó cho phép bạn đảm bảo rằng một lập trình viên đang sử dụng chức năng của bạn một cách chính xác trong quá trình thử nghiệm, nhưng sau đó loại bỏ nó trong sản xuất.

Giá trị của nó có phần bị hạn chế do bạn phải đảm bảo bạn thực hiện đường dẫn đó thông qua mã và bạn thường muốn xử lý thêm vấn đề bằng một iftuyên bố riêng trong sản xuất. assertlà hữu ích nhất trong các tình huống như: "Tôi muốn giải quyết vấn đề này một cách hữu ích nếu người dùng gặp phải nó, nhưng nếu nhà phát triển đánh nó, tôi muốn nó bị sập mạnh nên anh ta sẽ sửa mã gọi hàm này không chính xác."

Trong trường hợp cụ thể của bạn, một tệp bị thiếu gần như chắc chắn là lỗi người dùng và cần được xử lý bằng cách đưa ra một ngoại lệ.


5

Từ việc sử dụngAssertionsEffectively

Kiểm tra isinstance () không nên quá lạm dụng: nếu nó quẫy như một con vịt, có lẽ không cần phải tìm hiểu quá sâu về việc nó có thực sự như vậy không. Đôi khi nó có thể hữu ích để vượt qua các giá trị mà người lập trình ban đầu không lường trước được.

Những nơi cần xem xét đưa ra các xác nhận:

checking parameter types, classes, or values
checking data structure invariants
checking "can't happen" situations (duplicates in a list, contradictory state variables.)
after calling a function, to make sure that its return is reasonable 

Điểm chung là nếu có gì đó không ổn, chúng tôi muốn làm cho nó hoàn toàn rõ ràng càng sớm càng tốt.

Dễ dàng bắt được dữ liệu không chính xác tại thời điểm nó đi vào hơn là tìm ra cách nó đến đó sau khi nó gây rắc rối.

Các xác nhận không phải là một thay thế cho các bài kiểm tra đơn vị hoặc kiểm tra hệ thống, mà là một bổ sung. Bởi vì các xác nhận là một cách rõ ràng để kiểm tra trạng thái bên trong của một đối tượng hoặc chức năng, chúng cung cấp hỗ trợ "miễn phí" cho một thử nghiệm hộp đen kiểm tra hành vi bên ngoài.

Các xác nhận không nên được sử dụng để kiểm tra các trường hợp lỗi có thể xảy ra do đầu vào của người dùng xấu hoặc lỗi hệ điều hành / môi trường, chẳng hạn như không tìm thấy tệp. Thay vào đó, bạn nên đưa ra một ngoại lệ hoặc in thông báo lỗi hoặc bất cứ điều gì phù hợp. Một lý do quan trọng tại sao các xác nhận chỉ nên được sử dụng để tự kiểm tra chương trình là các xác nhận có thể bị vô hiệu hóa tại thời điểm biên dịch.

Nếu Python được bắt đầu với tùy chọn -O, thì các xác nhận sẽ bị loại bỏ và không được đánh giá. Vì vậy, nếu mã sử dụng các xác nhận rất nhiều, nhưng rất quan trọng về hiệu năng, thì có một hệ thống để tắt chúng trong các bản dựng phát hành. (Nhưng đừng làm điều này trừ khi nó thực sự cần thiết. Đã được khoa học chứng minh rằng một số lỗi chỉ xuất hiện khi khách hàng sử dụng máy và chúng tôi cũng muốn các xác nhận giúp đỡ ở đó .-))

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.