如果没有调用方法,Python会发出警告

时间:2018-04-01 21:38:59

标签: python debugging methods warnings invocation

假设您要在对象foo上调用方法bar,但在键入方法调用时,您会直观地将foo视为属性,而是键入bar.foo bar.foo()的(带括号)。现在,两者在语法上都是正确的,因此不会引发错误,但语义上非常不同。它已经好几次发生在我身上了(我在Ruby中的经历使它变得更糟)并且在长期和令人困惑的调试会话方面引起了我的不满。

有没有办法让Python解释器在这种情况下打印警告 - 每当你访问一个可调用的属性,但你实际上没有调用它?

为了记录 - 我考虑重写__getattribute__,但它很混乱并最终无法实现目标,因为()之后通过__getattribute__进行函数调用返回。

2 个答案:

答案 0 :(得分:2)

在所有情况下均无法执行此操作,因为有时您不想调用该方法,例如您可能希望将其存储为稍后要使用的可调用对象,例如callback = object.method

但你可以使用静态分析工具,如pylint或PyCharm(我的推荐),如果你写一个看起来毫无意义的陈述,警告你,例如object.method没有任何转让。

此外,如果你写x = obj.get_x但意味着get_x(),那么稍后当你尝试使用x时,静态分析工具可能会警告你(如果你很幸运的话) )x是一种方法,但需要X的实例。

答案 1 :(得分:2)

这非常具有挑战性,但我想我已经完成了!我的代码并不复杂,但你需要很好地了解元类。

元类和包装器(WarnIfNotCalled.py):

class Notifier:                                                             
    def __init__(self, name, obj, callback):                                
        self.callback = callback                                            
        self.name = name                                                    
        self.obj = obj                                                      
        self.called = False                                                 
    def __call__(self, *args, **kwargs):                                    
        self.callback(self.obj, *args, **kwargs)                            
        self.called = True                                                  
    def __del__(self):                                                      
        if not self.called:                                                 
            print("Warning! {} function hasn't been called!".format(self.name))

class WarnIfNotCalled(type):                                                
    def __new__(cls, name, bases, dct):                                     
        dct_func = {}                                                       
        for name, val in dct.copy().items():                                
            if name.startswith('__') or not callable(val):                  
                continue                                                    
            else:                                                           
                dct_func[name] = val                                        
                del dct[name]                                               
        def getattr(self, name):                                            
            if name in dct_func:                                            
                return Notifier(name, self, dct_func[name])                 
        dct['__getattr__'] = getattr                                        
        return super(WarnIfNotCalled, cls).__new__(cls, name, bases, dct) 

它非常易于使用 - 只需指定元类

即可
from WarnIfNotCalled import WarnIfNotCalled

class A(metaclass = WarnIfNotCalled):                                       
    def foo(self):                                                          
        print("foo has been called")                                        
    def bar(self, x):                                                       
        print("bar has been called and x =", x)

如果您没有忘记调用这些功能,那么一切正常

a = A()                                                                         

a.foo()                                                                         
a.bar(5)

输出:

foo has been called
bar has been called and x = 5

但如果你忘记了:

a = A()                                                                         

a.foo                                                                           
a.bar  

您会看到以下内容

Warning! foo function hasn't been called!
Warning! bar function hasn't been called!

快乐的调试!