API能否让Pylint在客户端代码中不报错?
我有一些代码在一个可以重复使用的类里,这段代码会修改一些类型。下面是一个简化的版本。
class Foo:
def __init__(self):
self.count = 0
def increment(self):
self.count += 1
# Add another method outside of the class definition.
# Pylint doesn't care about this, and rates this file 10/10.
Foo.__dict__["current_count"] = lambda self: self.count
在真实的代码中,“current_count”是一个变量,而不是一个固定的字符串,这就是我没有写成这样的原因:
Foo.current_count = lambda self: self.count # Cannot do in my scenario.
现在,当我的客户来使用这个新功能时,Pylint(一个代码检查工具)就像见了鬼一样大喊大叫。
import server_api
def main():
foo_count = server_api.Foo()
foo_count.increment()
print foo_count.current_count()
# Pylint complains here:
# E1101: 8:main: Instance of 'Foo' has no 'current_count' member
# I don't want to have to tell pylint to disable that message in every client.
main()
每个使用这个新功能的类都会被指责,我不得不在每个引用的地方关闭这个提示。我更希望在API里加一些代码,让Pylint在这个类有未知引用时能冷静点。
可惜的是,pylint的文档……嗯……质量不太好,让我很难理解,我在里面找不到任何建议。
所以简单来说:我能不能在我的API代码里告诉pylint,在客户引用这个类时,关闭与这个类相关的E1101规则?有没有其他的解决办法?
3 个回答
从实际角度来看,为什么大家会觉得pylint(或者其他类似工具)应该保持安静呢?在错误判断(假阳性和假阴性)之间,如果要选择,工具应该更倾向于报告错误。看起来是因为Pylint把结果显示成一个分数,人们就觉得应该尽量把这个分数提高,但其实并没有什么奖励给“获胜者”。
另一方面,它所抱怨的代码确实有点丑。我明白server_api是为了方便我们而简化的,但你真的需要去弄乱模块的命名空间吗?从你的客户端代码来看,current_count
这个方法名是写死在代码里的,为什么不把它放在服务器里呢?
根据你的一条评论,既然你想使用枚举类型,为什么不看看这个StackOverflow的问题,或者这个ActiveState的食谱呢?
出于个人喜好,我会选择把枚举类型放到类里面,就像那个问题中的一个答案一样(为了上下文,我在这里抄了一段):
class Animal:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def __repr__(self):
return "<Animal: %s>" % self
Animal.DOG = Animal("dog")
Animal.CAT = Animal("cat")
这是我根据ActiveState食谱中的例子,受到启发后提出的解决方案,来自于Yoni H的回答。
在Foo类中,我添加了这个没什么用的__getattr__
方法。
def __getattr__(self, name):
# This is only called when the normal mechanism fails, so in practice should never be called.
# It is only provided to satisfy pylint that it is okay not to raise E1101 errors in the client code.
raise AttributeError("%r instance has no attribute %r" % (self, name))
这个方法和之前的版本几乎没有区别。正常情况下不应该被调用,但它足以让pylint对这个错误保持沉默。
顺便说一句,你可以抱怨这段代码看起来不太美观。我也这么认为。但我觉得它对客户端的好处大于它的代码味道。