我正在查看Python: Exception in the separated module works wrong,它使用多用途的GnuLibError类来“代替”各种不同的错误。每个子错误都有自己的ID号和错误格式字符串。
我认为最好将其编写为Exception类的层次结构,并着手这样做:
class GNULibError(Exception):
sub_exceptions = 0 # patched with dict of subclasses once subclasses are created
err_num = 0
err_format = None
def __new__(cls, *args):
print("new {}".format(cls)) # DEBUG
if len(args) and args[0] in GNULibError.sub_exceptions:
print(" factory -> {} {}".format(GNULibError.sub_exceptions[args[0]], args[1:])) # DEBUG
return super(GNULibError, cls).__new__(GNULibError.sub_exceptions[args[0]], *(args[1:]))
else:
print(" plain {} {}".format(cls, args)) # DEBUG
return super(GNULibError, cls).__new__(cls, *args)
def __init__(self, *args):
cls = type(self)
print("init {} {}".format(cls, args)) # DEBUG
self.args = args
if cls.err_format is None:
self.message = str(args)
else:
self.message = "[GNU Error {}] ".format(cls.err_num) + cls.err_format.format(*args)
def __str__(self):
return self.message
def __repr__(self):
return '{}{}'.format(type(self).__name__, self.args)
class GNULibError_Directory(GNULibError):
err_num = 1
err_format = "destination directory does not exist: {}"
class GNULibError_Config(GNULibError):
err_num = 2
err_format = "configure file does not exist: {}"
class GNULibError_Module(GNULibError):
err_num = 3
err_format = "selected module does not exist: {}"
class GNULibError_Cache(GNULibError):
err_num = 4
err_format = "{} is expected to contain gl_M4_BASE({})"
class GNULibError_Sourcebase(GNULibError):
err_num = 5
err_format = "missing sourcebase argument: {}"
class GNULibError_Docbase(GNULibError):
err_num = 6
err_format = "missing docbase argument: {}"
class GNULibError_Testbase(GNULibError):
err_num = 7
err_format = "missing testsbase argument: {}"
class GNULibError_Libname(GNULibError):
err_num = 8
err_format = "missing libname argument: {}"
# patch master class with subclass reference
# (TO DO: auto-detect all available subclasses instead of hardcoding them)
GNULibError.sub_exceptions = {
1: GNULibError_Directory,
2: GNULibError_Config,
3: GNULibError_Module,
4: GNULibError_Cache,
5: GNULibError_Sourcebase,
6: GNULibError_Docbase,
7: GNULibError_Testbase,
8: GNULibError_Libname
}
这从GNULibError开始作为工厂类 - 如果使用属于可识别子类的错误号调用它,它将返回属于该子类的对象,否则它将自身返回为默认错误类型。
基于此代码,以下内容应完全等效(但不是):
e = GNULibError(3, 'missing.lib')
f = GNULibError_Module('missing.lib')
print e # -> '[GNU Error 3] selected module does not exist: 3'
print f # -> '[GNU Error 3] selected module does not exist: missing.lib'
我添加了一些战略性的印刷语句,错误似乎出现在GNULibError.__new__
:
>>> e = GNULibError(3, 'missing.lib')
new <class '__main__.GNULibError'>
factory -> <class '__main__.GNULibError_Module'> ('missing.lib',) # good...
init <class '__main__.GNULibError_Module'> (3, 'missing.lib') # NO!
^
why?
我将子类构造函数称为subclass.__new__(*args[1:])
- 这应该删除3,子类类型ID - 然而它的__init__
仍然得到3!如何修剪传递给subclass.__init__
的参数列表?
答案 0 :(得分:2)
您不能影响传递给__init__
的内容,只要您使用“工厂类”进行操作,就像您现在正在返回自身的子类一样。仍然传递“3”参数的原因是因为您仍然从__new__
返回GNULibError的实例。在调用__new__
时,确定将传递给__init__
的内容为时已晚。正如the documentation中所述(强调补充):
如果
__new__
()返回cls的实例,那么将调用新实例的__init__
()方法,如__init__(self[, ...])
,其中self是新实例和其余参数与传递给__new__()
的参数相同。
换句话说,当你调用GNULibError(3, 'missing.lib')
时,为时已晚 - 通过使用这些参数调用类,您已确保这些参数将传递给__init__
。 __new__
可以返回与您可能获得的实例不同的实例,但它无法阻止正常的初始化发生。
根据@Ned Batchelder的建议,你最好使用工厂功能而不是“工厂类”,因为一个功能没有这个__new__
/ __init__
机器你可以返回你想要的类的实例。
答案 1 :(得分:0)
这比它需要的更复杂。不要试图让类创建另一个类的对象。编写工厂函数来创建异常,不要乱用__new__
。你发现它太棘手了。
答案 2 :(得分:0)
在您的使用案例中 - 我同意Ned - 它需要更加复杂。
您可以尝试这样的事情(基于您的派生类似乎没有做任何事情但与错误消息不同的事实)。
class GNULibError(Exception):
pass # put logic code here
GNULibErrors = {
1: type('GNULibError_Directory', (GNULibError,), {'message': 'suitable whatever here'})
}
从那里调整......