我从python2.7打包了一个基于XML的远程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__
的参数实例化特定的子类呢
发布于 2012-09-25 22:30:24
创建一个函数,该函数将根据描述生成请求的错误类。如下所示:
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-25 22:32:44
工厂函数将更容易理解。使用字典将代码映射到异常类:
exceptions = {
123: NotFoundError,
456: NotWorkingError,
# ...
}
def exceptionFactory(description, code):
return exceptions[code](description, code)
发布于 2012-09-25 22:29:46
您可以创建一系列子类,并使用基类的__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)
https://stackoverflow.com/questions/12585115
复制相似问题