Cara menggunakan ASSERTING pada Python

Bisakah saya membuat assert melempar pengecualian yang saya pilih alih-alih AssertionError?

PEMBARUAN:

Saya akan menjelaskan motivasi saya: Sampai sekarang, saya sudah menjalani tes gaya pernyataan yang menimbulkan pengecualian saya sendiri; Misalnya, ketika Anda membuat objek Node dengan argumen tertentu, itu akan memeriksa apakah argumen itu baik untuk membuat node, dan jika tidak akan meningkatkan NodeError.

Tapi saya tahu bahwa Python memiliki mode -o di mana menegaskan dilewati, yang saya ingin tersedia karena itu akan membuat program saya lebih cepat. Tetapi saya masih ingin memiliki pengecualian sendiri. Itu sebabnya saya ingin menggunakan pernyataan dengan pengecualian saya sendiri.

Ini akan bekerja Tapi ini agak gila.

try:
    assert False, "A Message"
except AssertionError, e:
    raise Exception( e.args )

Kenapa tidak mengikuti yang berikut? Ini tidak gila.

if not someAssertion: raise Exception( "Some Message" )

Itu hanya sedikit lebih banyak daripada pernyataan assert, tetapi tidak melanggar harapan kami bahwa menyatakan kegagalan meningkatkan AssertionError.

Pertimbangkan ini.

def myAssert( condition, action ):
    if not condition: raise action

Maka Anda dapat lebih atau kurang mengganti pernyataan yang ada dengan sesuatu seperti ini.

myAssert( {{ the original condition }}, MyException( {{ the original message }} ) )

Setelah Anda melakukan ini, Anda sekarang bebas untuk sibuk dengan mengaktifkan atau menonaktifkan atau apa pun yang Anda coba lakukan.

Juga, baca di modul peringatan . Ini mungkin persis apa yang Anda coba lakukan.

Bagaimana dengan ini?


>>> def myraise(e): raise e
... 
>>> cond=False
>>> assert cond or myraise(RuntimeError)
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 1, in myraise
RuntimeError

Python juga melewatkan if __debug__: blok ketika dijalankan dengan opsi -o. Kode berikut ini lebih verbose, tetapi melakukan apa yang Anda butuhkan tanpa peretasan:

def my_assert(condition, message=None):
    if not condition:
        raise MyAssertError(message)

if __debug__: my_assert(condition, message)

Anda dapat membuatnya lebih pendek dengan memindahkan kondisi if __debug__: di dalam my_assert(), tetapi kemudian akan dipanggil (tanpa tindakan apa pun di dalamnya) ketika optimasi diaktifkan.

Jangan pernah menggunakan pernyataan untuk logika! Hanya untuk pemeriksaan pengujian opsional. Ingat, jika Python berjalan dengan optimisasi dihidupkan, menegaskan bahkan tidak dikompilasi ke dalam bytecode. Jika Anda melakukan ini, Anda jelas peduli dengan pengecualian yang diajukan dan jika Anda peduli, maka Anda menggunakan pernyataan salah sejak awal.

Anda dapat membiarkan manajer konteks melakukan konversi untuk Anda, di dalam blok with (yang dapat berisi lebih dari satu pernyataan, atau lebih banyak panggilan kode dan fungsi atau apa yang Anda inginkan.

from __future__ import with_statement
import contextlib

@contextlib.contextmanager
def myassert(exctype):
    try:
        yield
    except AssertionError, exc:
        raise exctype(*exc.args)

with myassert(ValueError):
    assert 0, "Zero is bad for you"

Lihat versi sebelumnya dari jawaban ini untuk mengganti objek pengecualian yang dikonstruksi secara langsung (KeyError("bad key")), alih-alih menggunakan kembali argumen pernyataan.

Untuk melihat apakah percobaan memiliki overhead, saya mencoba eksperimen ini

di sini adalah myassert.py


def myassert(e):
    raise e

def f1(): #this is the control for the experiment cond=True

def f2(): cond=True try: assert cond, "Message" except AssertionError, e: raise Exception(e.args)

def f3(): cond=True assert cond or myassert(RuntimeError)

def f4(): cond=True if __debug__: raise(RuntimeError)


$ python -O -mtimeit -n100 -r1000 -s'import myassert' 'myassert.f1()'
100 loops, best of 1000: 0.42 usec per loop
$ python -O -mtimeit -n100 -r1000 -s'import myassert' 'myassert.f2()'
100 loops, best of 1000: 0.479 usec per loop
$ python -O -mtimeit -n100 -r1000 -s'import myassert' 'myassert.f3()'
100 loops, best of 1000: 0.42 usec per loop
$ python -O -mtimeit -n100 -r1000 -s'import myassert' 'myassert.f4()'
100 loops, best of 1000: 0.42 usec per loop

Setidaknya dalam Python 2.6.3, ini juga akan berfungsi:

class MyAssertionError (Exception):
    pass

AssertionError = MyAssertionError

assert False, "False"

Traceback (most recent call last):
  File "assert.py", line 8, in <module>
    assert False, "False"
__main__.MyAssertionError: False

Jika Anda ingin menggunakan konfirmasi untuk ini, ini sepertinya bekerja dengan cukup baik:

>>> def raise_(e): raise e
...
>>> x = -2
>>> assert x >= 0, raise_(ValueError('oops'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in raise_
ValueError: oops

Perhatikan bahwa dalam pernyataan tersebut, hal-hal setelah koma hanya akan dievaluasi jika kondisinya salah, sehingga ValueError hanya akan dibuat dan dimunculkan bila diperlukan.