我有一个"工厂",它使用参数元组创建类的实例:
def factory(cls, arg):
return cls(*arg)
class Foo(object):
def __init__(self, a, b):
pass
a = (1,2)
f = factory(Foo, a)
这样做效果很好,所以我决定(出于本问题范围之外的各种原因)添加对不以任何参数作为后备的类的支持,以便在现有代码中更广泛地使用。因此,我需要检测2-arg与0-arg构造函数的可用性,并适当地回退。鸭子打字似乎是"Python"的答案,效果很好:
def factory(cls, arg):
try:
return cls(*arg)
except TypeError:
print("Missing 2-arg, falling back to 0-arg ctor")
return cls()
class Foo(object):
def __init__(self,a,b):
pass
class Bar(object):
def __init__(self,a):
pass
a = (1,2)
f = factory(Foo, a)
b = factory(Bar, a)
然而,当某个__init__
函数内部出现错误时,问题就出现了。当0-arg和2-arg__init__
都不存在时,我试图提供帮助并发出警告:
def factory(cls, arg):
try:
return cls(*arg)
except TypeError:
print("%s Missing 2-arg, falling back to 0-arg ctor" % str(cls))
return cls()
class Foo(object):
def __init__(self,a,b):
iter(a) # Oops, TypeError
a = (1,2)
try:
f = factory(Foo, a)
except TypeError: # 0-arg must be missing too
print("Neither 2-arg nor 0-arg ctor exist, that's all we support, sorry")
但这里有一个致命的缺陷——我无法区分由于不存在适当的__init__
而引发的TypeError
和由于__init__
中更深层次的问题而引发的TypeError
。
我不是在寻找修复Foo
或Bar
的方法,而是在"工厂"或其调用方级别更准确地了解故障的方法。
我如何用程序判断TypeError
是没有过载匹配的结果,还是其他地方的故障?我现在最好的想法是用程序遍历堆栈轨迹并查看行号,这充其量是可怕的。
通过测试__init__
方法所需的参数数量,可以将两种失败模式区分开来。
class Foo(object):
def __init__(self,a,b):
pass
class Bar(object):
def __init__(self,a):
pass
class Baz(object):
def __init__(self):
pass
def ctr_arg_count(cls):
fn = getattr(cls, '__init__')
return len(inspect.getargspec(fn).args) - 1
def factory(cls, *args):
if len(args) != ctr_arg_count(cls):
print("Can't initialize %s - wrong number of arguments" % cls)
return None
return cls(*args)
print factory(Foo, 1, 2)
print factory(Bar, 1)
print factory(Baz)
print factory(Foo, 1)
>>> <__main__.Foo object at 0x100483d10>
<__main__.Bar object at 0x100483d10>
<__main__.Baz object at 0x100483d10>
Can't initialize <class '__main__.Foo'> - wrong number of arguments
None