0

Python 2.7 からリモート XML ベースの API をラップしています。API は、<statusCode>要素だけでなく要素も送信することでエラーをスローし<statusDescription>ます。現時点では、この条件をキャッチして、単一の例外タイプを発生させます。何かのようなもの:

class ApiError(Exception):
    pass

def process_response(response):
    if not response.success:
        raise ApiError(response.statusDescription)

これは問題なく動作しますが、より洗練された方法でエラーを処理したいと考えています。要素があるのでstatusCode、statusCode に基づいて ApiError の特定のサブクラスを発生させたいと思います。事実上、ラッパーを次のように拡張したいと考えています。

class ApiError(Exception):
    def __init__(self, description, code):
        # How do I change self to be a different type?
        if code == 123:
            return NotFoundError(description, code)
        elif code == 456:
            return NotWorkingError(description, code)

class NotFoundError(ApiError):
    pass

class NotWorkingError(ApiError):
    pass

def process_response(response):
    if not response.success:
        raise ApiError(response.statusDescription, response.statusCode)

def uses_the_api():
    try:
        response = call_remote_api()
    except NotFoundError, e:
        handle_not_found(e)
    except NotWorkingError, e:
        handle_not_working(e)

特定の を特定のサブクラスに結び付けるstatusCode仕組みは簡単です。しかし、私が望むのは、それが ApiError のどこかに埋め込まれていることです。具体的には、値を渡す以外に process_response を変更したくありませんstatusCode

私はメタクラスを見てきましたが、__new__実行時の引数ではなく書き込み時の引数を取得するため、メタクラスが状況に役立つかどうかはわかりません。__init__同様に、インスタンスを返すことを意図していないため、ハッキングも役に立ちません。では、渡された引数に基づいて特定のサブクラスをインスタンス化するにはどうすればよい__init__でしょうか?

4

3 に答える 3

3

A factory function is going to be much easier to understand. Use a dictionary to map codes to exception classes:

exceptions = {
    123: NotFoundError,
    456: NotWorkingError,
    # ...
}

def exceptionFactory(description, code):
    return exceptions[code](description, code)
于 2012-09-25T14:32:43.640 に答える
1

説明に基づいて、要求されたエラー クラスを生成する関数を作成します。このようなもの:

def get_valid_exception(description, code):
    if code == 123:
        return NotFoundError(description, code)
    elif code == 456:
        return NotWorkingError(description, code)

要件と将来の変更に応じて、この関数を使用するコードに影響を与えることなく、異なる引数で例外を作成したり、他のことを行うことができます。

次に、コードで次のように使用できます。

def process_response(response):
    if not response.success:
        raise get_valid_exception(response.statusDescription, response.statusCode)
于 2012-09-25T14:30:23.943 に答える
1

一連のサブクラスを作成し、基本クラス__new__を子のファクトリとして使用できます。ただし、これはおそらくやり過ぎです。単純なファクトリ メソッドまたはクラスを作成するだけです。ただし、別の方向に凝りたい場合は、サブクラスが作成されたときに自動的にファクトリに追加される基本クラスのメタクラスを作成できます。何かのようなもの:

class ApiErrorRegistry(type):

    code_map = {}

    def __new__(cls, name, bases, attrs):

        try:
            mapped_code = attrs.pop('__code__')
        except KeyError:
            if name != 'ApiError':
                raise TypeError('ApiError subclasses must define a __code__.')
            mapped_code = None
        new_class = super(ApiErrorRegistry, cls).__new__(cls, name, bases, attrs)
        if mapped_code is not None:
            ApiErrorRegistry.code_map[mapped_code] = new_class
        return new_class

def build_api_error(description, code):

    try:
        return ApiErrorRegistry.code_map[code](description, code)
    except KeyError:
        raise ValueError('No error for code %s registered.' % code)


class ApiError(Exception):

    __metaclass__ = ApiErrorRegistry


class NotFoundError(ApiError):

    __code__ = 123


class NotWorkingError(ApiError):

    __code__ = 456


def process_response(response):

    if not response.success:
        raise build_api_error(response.statusDescription, response.statusCode)

def uses_the_api():
    try:
        response = call_remote_api()
    except ApiError as e:
        handle_error(e)
于 2012-09-25T14:29:45.760 に答える