Tôi nên đưa ra ngoại lệ nào đối với các kết hợp đối số xấu / bất hợp pháp trong Python?


544

Tôi đã tự hỏi về các thực tiễn tốt nhất để chỉ ra các kết hợp đối số không hợp lệ trong Python. Tôi đã gặp một vài tình huống trong đó bạn có một chức năng như vậy:

def import_to_orm(name, save=False, recurse=False):
    """
    :param name: Name of some external entity to import.
    :param save: Save the ORM object before returning.
    :param recurse: Attempt to import associated objects as well. Because you
        need the original object to have a key to relate to, save must be
        `True` for recurse to be `True`.
    :raise BadValueError: If `recurse and not save`.
    :return: The ORM object.
    """
    pass

Khó chịu duy nhất với điều này là mọi gói đều có cái riêng, thường hơi khác nhau BadValueError. Tôi biết rằng trong Java tồn tại java.lang.IllegalArgumentException- có hiểu rõ rằng mọi người sẽ tạo ra BadValueErrors của riêng họ trong Python hoặc có một phương thức ưa thích khác không?

Câu trả lời:


608

Tôi sẽ chỉ tăng ValueError , trừ khi bạn cần một ngoại lệ cụ thể hơn ..

def import_to_orm(name, save=False, recurse=False):
    if recurse and not save:
        raise ValueError("save must be True if recurse is True")

Thực sự không có điểm nào trong việc thực hiện class BadValueError(ValueError):pass- lớp tùy chỉnh của bạn giống hệt với ValueError , vậy tại sao không sử dụng nó?


65
> "vậy tại sao không sử dụng nó?" - Tính đặc hiệu. Có lẽ tôi muốn bắt ở lớp ngoài "MyValueError", nhưng không phải bất kỳ / tất cả "ValueError".
Kevin Little

7
Vâng, vì vậy một phần của câu hỏi về tính đặc hiệu là nơi nào khác ValueError được nêu ra. Nếu hàm callee thích các đối số của bạn nhưng gọi math.sqrt (-1) trong nội bộ, một người gọi có thể đang bắt ValueError mong rằng các đối số của nó không phù hợp. Có lẽ bạn chỉ cần kiểm tra được thông báo trong trường hợp này ...
cdleary

3
Tôi không chắc rằng đối số đó đúng: nếu ai đó đang gọi math.sqrt(-1), đó là lỗi lập trình cần phải được sửa. ValueErrorkhông có ý định bị bắt trong thực thi chương trình bình thường hoặc nó sẽ xuất phát từ RuntimeError.
vào

2
Nếu lỗi nằm ở SỐ đối số, đối với hàm có số lượng đối số thay đổi ... ví dụ: hàm phải là số đối số chẵn, thì bạn nên tăng TypeError, để thống nhất. Và đừng tạo lớp của riêng bạn trừ khi a) bạn có trường hợp sử dụng hoặc b) bạn đang xuất thư viện để người khác sử dụng. Chức năng sớm là cái chết của mã.
Erik Aronesty

104

Tôi sẽ thừa hưởng từ ValueError

class IllegalArgumentError(ValueError):
    pass

Đôi khi tốt hơn là tạo ra các ngoại lệ của riêng bạn, nhưng kế thừa từ một tích hợp sẵn, gần với những gì bạn muốn nhất có thể.

Nếu bạn cần phải bắt lỗi cụ thể đó, thật hữu ích khi có một cái tên.


26
Dừng viết các lớp và ngoại lệ tùy chỉnh - pyvideo.org/video/880/stop-wr- classes
Hamish Grubijan

40
@ HamishGrubijan rằng video đó là khủng khiếp. Khi bất cứ ai đề nghị sử dụng tốt một lớp học, anh ta chỉ nói "Đừng sử dụng các lớp học". Xuất sắc. Các lớp học là tốt. Nhưng đừng hiểu ý tôi .
Rob Grant

12
@RobertGrant Không, bạn không hiểu. Video đó không thực sự là về "nghĩa đen không sử dụng các lớp". Đó là về những điều không quá phức tạp.
RayLuo

15
@RayLuo bạn có thể đã kiểm tra xem video nói gì và chuyển đổi nó thành một thông điệp thay thế hợp lý, hợp lý, nhưng đó là những gì video nói và đó là điều mà một người không có nhiều kinh nghiệm và ý thức thông thường sẽ biến mất với.
Rob Grant

3
@SamuelSantana như tôi đã nói, bất cứ khi nào có ai đưa tay lên và nói "còn X thì sao?" Trong đó X là một ý tưởng hay, anh ta chỉ nói, "đừng tạo ra một lớp học khác." Khá rõ ràng. Tôi đồng ý chìa khóa là sự cân bằng; vấn đề là quá mơ hồ để thực sự sống bằng cách :-)
Rob Grant

18

Tôi nghĩ cách tốt nhất để xử lý việc này là cách con trăn tự xử lý nó. Python tăng TypeError. Ví dụ:

$ python -c 'print(sum())'
Traceback (most recent call last):
File "<string>", line 1, in <module>
TypeError: sum expected at least 1 arguments, got 0

Nhà phát triển cơ sở của chúng tôi vừa tìm thấy trang này trong một tìm kiếm của Google về "đối số sai ngoại lệ python" và tôi ngạc nhiên rằng câu trả lời rõ ràng (với tôi) đã không được đề xuất trong thập kỷ kể từ khi câu hỏi này được hỏi.


8
Không có gì làm tôi ngạc nhiên nhưng tôi đồng ý 100% rằng TypeError là ngoại lệ chính xác nếu loại sai trên một số đối số được truyền vào hàm. ValueError sẽ phù hợp nếu các biến có loại chính xác nhưng nội dung và giá trị của chúng không có ý nghĩa.
dùng3504575

Tôi nghĩ rằng điều này có lẽ là thiếu hoặc chưa được giải thích cho các đối số, trong khi câu hỏi là về các đối số được đưa ra chính xác, nhưng không chính xác ở mức độ trừu tượng cao hơn liên quan đến giá trị của đối số đã cho. Nhưng vì tôi thực sự đang tìm kiếm cái trước, nên dù sao cũng có một upvote.
Không ai

2
Như @ user3504575 và @Nobody đã nói, TypeError được sử dụng nếu các đối số không khớp với chữ ký hàm (sai số đối số vị trí, đối số từ khóa với tên sai, loại đối số sai), nhưng ValueError được sử dụng khi hàm gọi khớp với chữ ký nhưng các giá trị đối số không hợp lệ (ví dụ: gọi int('a')). nguồn
goodmami

Vì câu hỏi của OP đề cập đến "các kết hợp đối số không hợp lệ", có vẻ như TypeError sẽ phù hợp vì đây sẽ là trường hợp chữ ký hàm về cơ bản là sai đối với các đối số được truyền.
J Bones

Ví dụ của bạn gọi sum()không có đối số, đó là một TypeError, nhưng OP quan tâm đến các kết hợp giá trị đối số "bất hợp pháp" khi các loại đối số là chính xác. Trong trường hợp này, cả hai saverecurselà bools, nhưng nếu recurseTruesau đó savekhông nên False. Đây là một ValueError. Tôi đồng ý rằng một số giải thích về tiêu đề của câu hỏi sẽ được trả lời TypeError, nhưng không phải cho ví dụ được trình bày.
goodmami

11

Tôi hầu như chỉ thấy nội dung ValueErrorđược sử dụng trong tình huống này.


8

Nó phụ thuộc vào vấn đề với các đối số là gì.

Nếu đối số có loại sai, hãy nâng TypeError. Ví dụ: khi bạn nhận được một chuỗi thay vì một trong các Booleans đó.

if not isinstance(save, bool):
    raise TypeError(f"Argument save must be of type bool, not {type(save)}")

Tuy nhiên, lưu ý rằng trong Python chúng tôi hiếm khi thực hiện bất kỳ kiểm tra nào như thế này. Nếu đối số thực sự không hợp lệ, một số chức năng sâu hơn có thể sẽ làm phàn nàn cho chúng ta. Và nếu chúng ta chỉ kiểm tra giá trị boolean, có lẽ một số người dùng mã sau này sẽ cung cấp cho nó một chuỗi biết rằng các chuỗi không trống luôn luôn là True. Nó có thể cứu anh ta một diễn viên.

Nếu các đối số có giá trị không hợp lệ, hãy nâng cao ValueError. Điều này có vẻ phù hợp hơn trong trường hợp của bạn:

if recurse and not save:
    raise ValueError("If recurse is True, save should be True too")

Hoặc trong trường hợp cụ thể này, có giá trị True của recurse ngụ ý giá trị True của tiết kiệm. Vì tôi sẽ coi đây là sự phục hồi từ một lỗi, bạn cũng có thể muốn khiếu nại trong nhật ký.

if recurse and not save:
    logging.warning("Bad arguments in import_to_orm() - if recurse is True, so should save be")
    save = True

Tôi nghĩ rằng đây là câu trả lời chính xác nhất. Điều này rõ ràng là bị đánh giá thấp (7 phiếu cho đến nay bao gồm cả của tôi).
Siu Ching Pong -Asuka Kenji-

-1

Tôi không chắc chắn tôi đồng ý với việc thừa kế từ ValueError- cách giải thích của tôi về tài liệu ValueErrornày chỉ được cho là được xây dựng bởi các nội dung ... kế thừa từ nó hoặc tự nâng cao nó có vẻ không chính xác.

Được nâng lên khi một hoạt động hoặc hàm tích hợp nhận được một đối số có loại đúng nhưng giá trị không phù hợp và tình huống không được mô tả bằng một ngoại lệ chính xác hơn như IndexError.

- Tài liệu ValueError


So sánh google.com/codesearch?q=lang:python+ class \ + \ w Lỗi (([^ E] \ w * | E [^ x] \ w )): với google.com/codesearch?q=lang: python + class \ + \ w * Error (Ngoại lệ):
Markus Jarderot

13
Điều đó đơn giản có nghĩa là các công cụ xây dựng nâng cao nó, và không chỉ có các công cụ xây dựng mới có thể nâng cao nó. Trong trường hợp này, tài liệu Python sẽ không hoàn toàn thích hợp để nói về những gì các thư viện bên ngoài nêu ra.
Ignacio Vazquez-Abrams

5
Mọi phần mềm Python tôi từng thấy đều được sử dụng ValueErrorcho loại điều này, vì vậy tôi nghĩ bạn đang cố đọc quá nhiều vào tài liệu.
James Bennett

6
Err, nếu chúng tôi sẽ sử dụng các tìm kiếm Google Code để tranh luận về điều này: google.com/codesearch?q=lang%3Apython+raise%5C+ValueError # 66.300 trường hợp tăng ValueError, bao gồm Zope, xen, Django, Mozilla (và đó chỉ là từ trang đầu tiên của kết quả). Nếu một ngoại lệ dựng sẵn phù hợp, hãy sử dụng nó ..
dbr

7
Như đã nêu, các tài liệu là mơ hồ. Nó nên được viết là "Tăng khi hoạt động tích hợp hoặc chức năng tích hợp nhận được" hoặc "Tăng khi chức năng hoặc hoạt động tích hợp nhận được". Tất nhiên, dù mục đích ban đầu là gì, thực tế hiện tại đã thổi phồng nó (như @dbr chỉ ra). Vì vậy, nó nên được viết lại như là biến thể thứ hai.
Eponymous

-1

Đồng ý với đề xuất của Markus để đưa ra ngoại lệ của riêng bạn, nhưng văn bản của ngoại lệ sẽ làm rõ rằng vấn đề nằm trong danh sách đối số, không phải các giá trị đối số riêng lẻ. Tôi muốn đề xuất:

class BadCallError(ValueError):
    pass

Được sử dụng khi thiếu đối số từ khóa được yêu cầu cho cuộc gọi cụ thể hoặc giá trị đối số là hợp lệ riêng lẻ nhưng không nhất quán với nhau. ValueErrorvẫn sẽ đúng khi một đối số cụ thể là loại đúng nhưng nằm ngoài phạm vi.

Đây có phải là một ngoại lệ tiêu chuẩn trong Python không?

Nói chung, tôi muốn kiểu Python sắc nét hơn một chút trong việc phân biệt các đầu vào xấu với một hàm (lỗi của người gọi) với các kết quả xấu trong hàm (lỗi của tôi). Vì vậy, cũng có thể có một BadArgumentError để phân biệt lỗi giá trị trong các đối số với lỗi giá trị ở địa phương.


Tôi KeyErrorkhông thể tìm thấy từ khóa (vì một từ khóa rõ ràng bị thiếu giống hệt về mặt ngữ nghĩa với một từ **kwargsbị thiếu khóa đó).
cowbert
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.