Pythonで独自の例外クラスを定義する方法

こんにちは、Habr!



新しい本「PythonProSecrets」へのあなたの興味は、 Pythonの癖の物語が続くに値することを私たちに確信させました。今日は、カスタム(テキスト内-独自の)例外クラスの作成に関する小さなチュートリアルを読むことを提案します。著者はそれを面白くしました、例外の最も重要な利点がエラーメッセージの完全性と明快さであるということを彼に反対するのは難しいです。オリジナルのコードの一部は写真の形をしています。



猫へようこそ。



独自のエラークラスの作成



Pythonには、独自の例外クラスを作成する機能があります。このようなクラスを作成することにより、アプリケーション内のクラスの設計を多様化できます。エラーのカスタムクラスは、エラーをログに記録し、オブジェクトを検査する可能性があります。例外クラスが何をするかを定義するのは私たちですが、通常、カスタムクラスはメッセージを表示する以上のことはほとんどできません。



当然、エラータイプ自体は重要であり、通常はPython言語レベルではカバーされない特定の状況を示すために、独自のエラータイプを作成することがよくあります。このようにして、クラスのユーザーは、このエラーが発生したときに何が起こっているかを正確に知ることができます。



この記事は2部に分かれています。まず、例外クラスを単独で定義します。次に、独自の例外クラスをPythonプログラムに統合する方法を示し、設計したクラスの使いやすさを向上させる方法を示します。



MyCustomErrorカスタム例外クラス



例外をスローするには、 __init__()



andメソッドが必要__str__()



です。




例外をスローするとき、すでに例外のインスタンスを作成し、同時にそれを画面に表示します。以下に示すカスタム例外クラスを詳しく見てみましょう。







上記MyCustomErrorクラスは2つのマジックメソッド、あり __init__



__str__



自動的に例外処理中に呼び出され、。メソッド Init



はインスタンスの作成時に呼び出され、メソッドstr



インスタンスの表示時に呼び出さ れます。したがって、例外がスローされると、通常、これら2つのメソッドは互いに直後に呼び出されます。 Pythonの例外throwステートメントは、プログラムをエラー状態にします。



メソッドの引数リスト内 __init__



です *args



。コンポーネント *args



は、関数とメソッドで使用される特別なパターンマッチングモードです。複数の引数を渡すことができ、渡された引数をタプルとして格納できますが、同時に、引数をまったく渡さないようにすることもできます。



この場合、コンストラクターにMyCustomError



引数が渡された場合 、最初に渡された引数を取得message



して、オブジェクトの属性割り当て ます。引数が渡されなかった場合、属性に message



は値が割り当てられます None







最初の例では、例外MyCustomError



は引数なしでスローさ れるため、属性 message



このオブジェクトには値が割り当てられます None



メソッドが呼び出され str



、「MyCustomErrorメッセージが発生しました」というメッセージが表示されます。







例外 MyCustomError



は引数なしでスローされます(括弧は空です)。言い換えれば、そのようなオブジェクトのデザインは非標準に見えます。ただし、これは、例外をスローするときにPythonで提供される構文サポートにすぎません。



2番目の例で MyCustomError



は、文字列引数「問題があります」で渡されます。message



オブジェクトの属性として設定され 、例外がスローされるとエラーメッセージとして表示されます。







MyCustomError例外クラスのコードは こちらです..。



class MyCustomError(Exception):
    def __init__(self, *args):
        if args:
            self.message = args[0]
        else:
            self.message = None

    def __str__(self):
        print('calling str')
        if self.message:
            return 'MyCustomError, {0} '.format(self.message)
        else:
            return 'MyCustomError has been raised'


#  MyCustomError

raise MyCustomError('We have a problem')
      
      





CustomIntFloatDicクラス



独自の辞書を作成します。その値は整数と浮動小数点数のみです。



先に進んで、エラークラスを独自のプログラムに簡単かつ便利に挿入する方法を示しましょう。まず、少し工夫を凝らした例を紹介します。この架空の例では、整数または浮動小数点数のみを受け入れることができる独自の辞書を作成します。



ユーザーがこの辞書の値として他のデータタイプを指定しようとすると、例外がスローされます。この例外は、この辞書の使用方法に関する有用な情報をユーザーに提供します。私たちの場合、このメッセージは、整数または浮動小数点数のみがこの辞書の値として指定できることをユーザーに直接通知します。



独自の辞書を作成するときは、辞書に値を追加できる場所が2つあることに注意してください。まず、これはオブジェクトを作成するときのinitメソッドで発生する可能性があり(この段階では、オブジェクトにはすでにキーと値を割り当てることができます)、次に、キーと値を辞書に直接設定するときに発生する可能性があります。これらの場所の両方で、値がタイプint



または float



のみであることを保証するコードを記述する必要があります



まず、組み込みクラスから継承するCustomIntFloatDictクラスを定義します dict



dict



は、括弧で囲まれ、クラス名の後に続く引数のリストで渡され CustomIntFloatDict



ます。



クラスがインスタンス化されている場合 CustomIntFloatDict



さらに、キーと値のパラメータに引数は渡されず、に設定され None



ます。式は if



次のように解釈されます。キーが等しい None



か、値が等しい場合 None



、オブジェクトを使用してメソッドが呼び出され get_dict()



、属性が返されます empty_dict



。オブジェクトのそのような属性は、空のリストを指します。クラス属性は、クラスのすべてのインスタンスで使用できることに注意してください。







このクラスの目的は、ユーザーがキーと値を含むリストまたはタプルを渡すことができるようにすることです。ユーザーがキーと値を探しているリストまたはタプルを入力すると、これら2つの列挙されたセットは関数を使用して連結されます zip



Python言語。オブジェクトを指すフックされた変数 zip



は反復可能であり、タプルはアンパック可能です。タプルを反復処理することにより、valがクラスint



またはの インスタンスであるかどうかを確認します float



val



これらのクラスのいずれにも属しいない場合 IntFloatValueError



独自の例外をスローし 、valを引数として渡します。



IntFloatValueError例外クラス



例外がスローされるIntFloatValueError



と、クラスのインスタンスが作成さ れ、 IntFloatValueError



同時に画面に表示されます。これは、魔法のメソッドinit



が呼び出されることを意味し str



ます。



スローされた例外の原因となった値はvalue



、クラスに付随 する属性として設定されます IntFloatValueError



。 magic strメソッドを呼び出すと、ユーザーはの値が無効init



である ことを通知するエラーメッセージを受け取り CustomIntFloatDict



ます。ユーザーは、このエラーを修正するために何をすべきかを知っています。







例外クラス IntFloatValueError



および KeyValueConstructError











例外がスローされない場合、つまりすべて val



チェーンされたオブジェクトのタイプ int



または float



、の場合、それらはを使用して設定 され、以下に示すように__setitem__()



、親クラスのメソッドがすべてを実行し dict



ます。







KeyValueConstructErrorクラス



ユーザーがリストまたはキーと値のタプルではないタイプを入力するとどうなりますか?



繰り返しますが、この例は少し人工的なものですが、独自の例外クラスを使用する方法を示すと便利です。



ユーザーがキーと値をリストまたはタプルとして指定しない場合、例外がスローされ KeyValueConstructError



ます。この例外の目的は、キーと値をオブジェクトに書き込むためにCustomIntFloatDict



、リストまたはタプルをinit



クラス コンストラクターで指定する必要があることを ユーザーに通知することです CustomIntFloatDict







上記の例では、コンストラクターの2番目の引数として init



多くが渡され、このために例外がスローされました KeyValueConstructError



表示されるエラーメッセージの利点は、表示されるエラーメッセージが、挿入されるキーと値をリストまたはタプルとして報告する必要があることをユーザーに通知することです。



この場合も、例外がスローされると、KeyValueConstructErrorインスタンスが作成され、キーと値が引数としてKeyValueConstructErrorコンストラクターに渡されます。これらは、KeyValueConstructErrorのキーおよび値属性の値として設定され、メッセージが画面に表示されたときに意味のあるエラーメッセージを生成するために__str__メソッドで使用されます。



さらに、コンストラクターに追加されたオブジェクトに固有のデータタイプも含めます init



-わかりやすくするためにこれを行います。







CustomIntFloatDictでキーと値を設定する



CustomIntFloatDict



から継承し dict



ます。これは、動作を選択的に変更することを選択した場所を除いて、辞書とまったく同じように機能することを意味します。



__setitem__



辞書にキーと値を設定するときに呼び出される魔法のメソッドです。私たちの実装で setitem



は、値がタイプint



またはであるか どうかをチェックし、 float



チェックが成功した後にのみ、辞書に設定できます。チェックに失敗した場合は、例外クラスを再度使用できます IntFloatValueError



ここ‘bad_value’



で、辞書の値として 文字列を設定しようとするtest_4



と、例外が発生することを確認でき ます。







このチュートリアルのすべてのコードを以下に示し 、Githubに投稿します。



#  ,        int  float  

class IntFloatValueError(Exception):
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return '{} is invalid input, CustomIntFloatDict can only accept ' \
               'integers and floats as its values'.format(self.value)


class KeyValueContructError(Exception):
    def __init__(self, key, value):
        self.key = key
        self.value = value

    def __str__(self):
        return 'keys and values need to be passed as either list or tuple' + '\n' + \
                ' {} is of type: '.format(self.key) + str(type(self.key)) + '\n' + \
                ' {} is of type: '.format(self.value) + str(type(self.value))


class CustomIntFloatDict(dict):

    empty_dict = {}

    def __init__(self, key=None, value=None):

        if key is None or value is None:
            self.get_dict()

        elif not isinstance(key, (tuple, list,)) or not isinstance(value, (tuple, list)):
            raise KeyValueContructError(key, value)
        else:
            zipped = zip(key, value)
            for k, val in zipped:
                if not isinstance(val, (int, float)):
                    raise IntFloatValueError(val)
                dict.__setitem__(self, k, val)

    def get_dict(self):
        return self.empty_dict

    def __setitem__(self, key, value):
        if not isinstance(value, (int, float)):
            raise IntFloatValueError(value)
        return dict.__setitem__(self, key, value)


#  

# test_1 = CustomIntFloatDict()
# print(test_1)
# test_2 = CustomIntFloatDict({'a', 'b'}, [1, 2])
# print(test_2)
# test_3 = CustomIntFloatDict(('x', 'y', 'z'), (10, 'twenty', 30))
# print(test_3)
# test_4 = CustomIntFloatDict(('x', 'y', 'z'), (10, 20, 30))
# print(test_4)
# test_4['r'] = 1.3
# print(test_4)
# test_4['key'] = 'bad_value'
      
      





結論



独自の例外を作成すると、クラスでの作業がはるかに便利になります。例外クラスにはマジックメソッドが必要で init



あり str



、例外処理中に自動的に呼び出されます。あなた自身の例外クラスが何をするかは完全にあなた次第です。示されているメソッドの中には、オブジェクトを検査し、画面に有益なエラーメッセージを表示するためのメソッドがあります。



とはいえ、例外クラスを使用すると、発生したエラーの処理がはるかに簡単になります。



All Articles