假设您要在对象foo
上调用方法bar
,但在键入方法调用时,您会直观地将foo
视为属性,而是键入bar.foo
bar.foo()
的(带括号)。现在,两者在语法上都是正确的,因此不会引发错误,但语义上非常不同。它已经好几次发生在我身上了(我在Ruby中的经历使它变得更糟)并且在长期和令人困惑的调试会话方面引起了我的不满。
有没有办法让Python解释器在这种情况下打印警告 - 每当你访问一个可调用的属性,但你实际上没有调用它?
为了记录 - 我考虑重写__getattribute__
,但它很混乱并最终无法实现目标,因为()
之后通过__getattribute__
进行函数调用返回。
答案 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!
快乐的调试!