Pengecualian mana yang harus saya ajukan pada kombinasi argumen buruk / ilegal dengan Python?

544

Saya bertanya-tanya tentang praktik terbaik untuk menunjukkan kombinasi argumen yang tidak valid dalam Python. Saya telah menemukan beberapa situasi di mana Anda memiliki fungsi seperti:

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

Satu-satunya gangguan dengan ini adalah bahwa setiap paket memiliki sendiri, biasanya sedikit berbeda BadValueError. Saya tahu bahwa di Jawa ada java.lang.IllegalArgumentException- apakah dipahami bahwa semua orang akan membuat sendiri BadValueErrordengan Python atau ada metode lain yang disukai?

cdleary
sumber

Jawaban:

608

Saya hanya akan meningkatkan ValueError , kecuali jika Anda memerlukan pengecualian yang lebih spesifik ..

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

Benar-benar tidak ada gunanya melakukan class BadValueError(ValueError):pass- kelas kustom Anda identik digunakan untuk ValueError , jadi mengapa tidak menggunakannya?

dbr
sumber
65
> "jadi mengapa tidak menggunakannya?" - Kekhususan. Mungkin saya ingin menangkap lapisan luar "MyValueError", tetapi tidak semua / semua "ValueError".
Kevin Little
7
Ya, jadi bagian dari pertanyaan tentang kekhususan adalah di mana lagi ValueError dimunculkan. Jika fungsi callee suka argumen Anda, tetapi panggilan Math.sqrt (-1) secara internal, penelepon dapat menangkap ValueError berharap bahwa nya argumen yang tidak pantas. Mungkin Anda baru saja memeriksa pesan dalam kasus ini ...
cdleary
3
Saya tidak yakin argumen itu berlaku: jika seseorang menelepon math.sqrt(-1), itu adalah kesalahan pemrograman yang perlu diperbaiki. ValueErrortidak dimaksudkan untuk ditangkap dalam pelaksanaan program normal atau itu akan berasal dari RuntimeError.
sebelum
2
Jika kesalahan ada pada NUMBER argumen, untuk fungsi dengan jumlah variabel variabel ... misalnya fungsi di mana argumen harus berupa argumen genap, maka Anda harus menaikkan TypeError, agar konsisten. Dan jangan membuat kelas Anda sendiri kecuali a) Anda memiliki use case atau b) Anda mengekspor perpustakaan untuk digunakan oleh orang lain. Fungsionalitas prematur adalah kematian kode.
Erik Aronesty
104

Saya akan mewarisi dari ValueError

class IllegalArgumentError(ValueError):
    pass

Terkadang lebih baik untuk membuat pengecualian Anda sendiri, tetapi mewarisi dari bawaan, yang sedekat mungkin dengan apa yang Anda inginkan.

Jika Anda perlu mengetahui kesalahan spesifik itu, sebaiknya memiliki nama.

Markus Jarderot
sumber
26
Berhenti menulis kelas dan pengecualian khusus - pyvideo.org/video/880/stop-writing-classes
Hamish Grubijan
40
@HamishGrubijan video itu mengerikan. Ketika ada yang menyarankan penggunaan kelas dengan baik, ia hanya mengembik, "Jangan gunakan kelas." Cemerlang. Kelasnya bagus. Tapi jangan mengambil kata-kata saya untuk itu .
Rob Grant
12
@RobertGrant Tidak, Anda tidak mengerti. Video itu tidak benar-benar tentang "tidak menggunakan kelas". Ini tentang jangan terlalu rumit.
RayLuo
15
@RayLuo Anda mungkin telah memeriksa kewarasan apa yang dikatakan video dan mengubahnya menjadi pesan alternatif yang masuk akal, tetapi itulah yang dikatakan video tersebut, dan itulah yang seseorang yang tidak memiliki banyak pengalaman dan akal sehat akan pergi dengan.
Rob Grant
3
@SamuelSantana seperti yang saya katakan, kapan saja ada yang mengangkat tangan dan berkata "bagaimana dengan X?" di mana X adalah ide yang bagus, dia hanya berkata, "jangan membuat kelas lain." Cukup jelas. Saya setuju bahwa kuncinya adalah keseimbangan; masalahnya adalah itu terlalu samar untuk benar-benar hidup dengan :-)
Rob Grant
18

Saya pikir cara terbaik untuk menangani ini adalah cara python sendiri menanganinya. Python memunculkan TypeError. Sebagai contoh:

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

Dev junior kami baru saja menemukan halaman ini di pencarian google untuk "argumen salah python exception" dan saya terkejut bahwa jawaban yang jelas (untuk saya) tidak pernah disarankan dalam dekade sejak pertanyaan ini diajukan.

J Bones
sumber
8
Tidak ada yang mengejutkan saya, tetapi saya setuju 100% bahwa TypeError adalah pengecualian yang benar jika tipenya salah pada beberapa argumen yang masuk ke fungsi. ValueError akan cocok jika variabelnya bertipe benar tetapi konten dan nilainya tidak masuk akal.
user3504575
Saya pikir ini mungkin untuk argumen yang hilang atau tidak pantas, sementara pertanyaannya adalah tentang argumen yang diberikan dengan benar, tetapi salah pada tingkat abstraksi yang lebih tinggi yang melibatkan nilai dari argumen yang diberikan. Tapi karena saya benar-benar mencari yang pertama, jadi tetap saja pilih-pilih.
Tak seorang pun
2
Seperti kata @ user3504575 dan @Nobody, TypeError digunakan jika argumen tidak cocok dengan tanda tangan fungsi (jumlah argumen posisi salah, argumen kata kunci dengan nama yang salah, tipe argumen yang salah), tetapi ValueError digunakan ketika fungsi memanggil cocok dengan tanda tangan tetapi nilai argumen tidak valid (mis., panggilan int('a')). sumber
goodmami
Karena pertanyaan OP merujuk pada "kombinasi argumen yang tidak valid", sepertinya TypeError akan sesuai karena ini akan menjadi kasus di mana fungsi tanda tangan pada dasarnya salah untuk argumen yang disahkan.
J Bones
Contoh Anda memanggil sum()tanpa argumen, yang merupakan TypeError, tetapi OP khawatir dengan kombinasi nilai argumen "ilegal" ketika jenis argumen benar. Dalam hal ini, keduanya savedan recursebools, tetapi jika recurseitu Truemaka saveseharusnya tidak boleh False. Ini adalah ValueError. Saya setuju bahwa beberapa interpretasi dari judul pertanyaan akan dijawab oleh TypeError, tetapi tidak untuk contoh yang disajikan.
goodmami
11

Saya sebagian besar baru saja melihat builtin ValueErrordigunakan dalam situasi ini.

Eli Courtwright
sumber
8

Itu tergantung pada apa masalahnya dengan argumen itu.

Jika argumen memiliki tipe yang salah, naikkan TypeError. Misalnya, ketika Anda mendapatkan string, bukan salah satu Boolean tersebut.

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

Perhatikan, bagaimanapun, bahwa dalam Python kita jarang melakukan pemeriksaan seperti ini. Jika argumen tersebut benar-benar tidak valid, beberapa fungsi yang lebih dalam mungkin akan melakukan komplain untuk kita. Dan jika kita hanya memeriksa nilai boolean, mungkin beberapa pengguna kode nantinya hanya akan memberinya string yang mengetahui bahwa string yang tidak kosong selalu Benar. Mungkin menyelamatkannya gips.

Jika argumen memiliki nilai yang tidak valid, naikkan ValueError. Ini tampaknya lebih sesuai untuk kasus Anda:

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

Atau dalam kasus khusus ini, memiliki nilai True recurse menyiratkan nilai True save. Karena saya akan menganggap ini sebagai pemulihan dari kesalahan, Anda mungkin juga ingin mengeluh dalam log.

if recurse and not save:
    logging.warning("Bad arguments in import_to_orm() - if recurse is True, so should save be")
    save = True
Gloweye
sumber
Saya pikir ini adalah jawaban yang paling akurat. Ini jelas diremehkan (7 suara sejauh ini termasuk milik saya).
Siu Ching Pong -Asuka Kenji-
-1

Saya tidak yakin saya setuju dengan warisan dari ValueError- interpretasi saya dari dokumentasi adalah bahwa ValueErrorini hanya seharusnya dibesarkan oleh builtin ... mewarisi dari itu atau menaikkan sendiri sepertinya tidak benar.

Dibesarkan ketika operasi atau fungsi bawaan menerima argumen yang memiliki tipe yang tepat tetapi nilai yang tidak sesuai, dan situasinya tidak dijelaskan oleh pengecualian yang lebih tepat seperti IndexError.

- Dokumentasi ValueError

cdleary
sumber
Bandingkan google.com/codesearch?q=lang:python+class \ + \ w Kesalahan (([^ E] \ w * | E [^ x] \ w )): dengan google.com/codesearch?q=lang: python + class \ + \ w * Kesalahan (Pengecualian):
Markus Jarderot
13
Pinggiran itu hanya berarti bahwa built-in meningkatkannya, dan bukan hanya built-in yang dapat meningkatkannya. Dalam hal ini tidak sepenuhnya tepat untuk dokumentasi Python untuk berbicara tentang apa yang dibangkitkan oleh perpustakaan eksternal.
Ignacio Vazquez-Abrams
5
Setiap perangkat lunak Python yang pernah saya lihat digunakan ValueErroruntuk hal semacam ini, jadi saya pikir Anda mencoba membaca terlalu banyak ke dalam dokumentasi.
James Bennett
6
Err, jika kita akan menggunakan pencarian Google Code untuk memperdebatkan ini: google.com/codesearch?q=lang%3Apython+raise%5C+ValueError # 66.300 kasus meningkatkan ValueError, termasuk Zope, xen, Django, Mozilla (dan itu baru dari halaman pertama hasil). Jika pengecualian bawaan cocok, gunakan ..
dbr
7
Seperti yang dinyatakan, dokumentasinya ambigu. Seharusnya ditulis sebagai "Dibesarkan ketika operasi built-in atau fungsi built-in menerima" atau sebagai "Dibesarkan ketika fungsi atau operasi built-in menerima". Tentu saja, apa pun maksud aslinya, praktik saat ini telah mengacaukannya (seperti yang ditunjukkan @dbr). Jadi itu harus ditulis ulang sebagai varian kedua.
Eponim
-1

Setuju dengan saran Markus untuk melempar pengecualian Anda sendiri, tetapi teks pengecualian harus menjelaskan bahwa masalahnya ada dalam daftar argumen, bukan nilai argumen individual. Saya akan mengusulkan:

class BadCallError(ValueError):
    pass

Digunakan ketika argumen kata kunci hilang yang diperlukan untuk panggilan tertentu, atau nilai argumen secara individual valid tetapi tidak konsisten satu sama lain. ValueErrormasih akan benar ketika argumen tertentu adalah tipe yang benar tetapi di luar jangkauan.

Bukankah ini harus menjadi pengecualian standar dalam Python?

Secara umum, saya ingin gaya Python menjadi sedikit lebih tajam dalam membedakan input buruk ke fungsi (kesalahan pemanggil) dari hasil buruk dalam fungsi (kesalahan saya). Jadi mungkin juga ada BadArgumentError untuk membedakan kesalahan nilai dalam argumen dari kesalahan nilai di lokal.

BobHy
sumber
Saya akan meningkatkan KeyErrorkata kunci yang tidak ditemukan (karena kata kunci eksplisit yang hilang secara semantik identik dengan **kwargsdikt yang tidak memiliki kunci itu).
cowbert