2014-02-22 65 views
3

我有一个“工厂”创建使用的参数元组的类的实例:诊断错误时鸭打字失败

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 vs 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区分以及TypeError,这是因为__init__中更深的问题而引发的。

我不是在寻找修复FooBar的方法,而是在“工厂”或其调用者的层面上更为精确地了解故障的方法。

如何以编程方式确定TypeError是否是无过载匹配或其他位置失败的结果?我现在最好的想法是通过编程的方式走栈跟踪并查看行号,这在最好的情况下是可怕的。

回答

1

测试__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