我正在从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中。具体来说,除了传递值statusCode
之外,我不想更改process_response。
我查看了元类,但不确定它们是否有助于这种情况,因为__new__
获得了写时参数,而不是运行时参数。同样无益的是在__init__
周围进行攻击,因为它并不打算返回实例。那么,如何基于传递给__init__
?
答案 0 :(得分:3)
工厂功能将更容易理解。使用字典将代码映射到异常类:
exceptions = {
123: NotFoundError,
456: NotWorkingError,
# ...
}
def exceptionFactory(description, code):
return exceptions[code](description, code)
答案 1 :(得分: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)
答案 2 :(得分: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)