在我工作的地方,我们广泛使用SQLAlchemy。加班后,我们为模型开发了适合我们需求的基类。但是当需要花些时间整理我们的代码时,我们总是被警告不知所措而感到不知所措。但是到目前为止,我们只能通过generated-members
指令在全局范围内做到这一点,该指令往往会隐藏问题。
所以我开始怀疑:“我怎么教普林特?”
这里是情况:
from sqlalchemy.ext.declarative import declarative_base
class CustomBaseModel(object):
def feature(self):
pass
Model = declarative_base(cls=CustomBaseModel)
class Thing(Model):
id = Column(Integer, primary_key=True)
label = Column(String(64))
t = Thing()
t.feature() # Pylint says Thing has not `feature()` method.
所以我想告诉pylint实际上是Model,或多或少是CustomBaseModel。
因此,看来我应该在对declarative_base()
的调用的返回值上使用inference_tip
。但是我不确定如何进行。而且看起来API随时间变化了,我什么也没去。
我研究的另一种策略是将CustomBaseModel
上的属性复制到模型。但这行不通。确实,对于Pylint Model而言,似乎只是一个名字……它失去了对它的了解,并且不知道它是一类。
任何提示将不胜感激...
答案 0 :(得分:1)
如果您替换此:
Model = declarative_base(cls=CustomBaseModel)
具有这样的内容:
def base_decorator(cls):
return declarative_base(cls = cls)
@base_decorator
class Model(CustomBaseModel):
pass
这将导致类似于以下执行顺序:
class Model(CustomBaseModel):
pass
Model = declarative_base(cls = Model)
从功能上讲,它与示例代码中的直接调用相同,但是它为pylint
提供了一个线索,即Model
是从CustomBaseModel
派生的。
答案 1 :(得分:0)
以下是我最敢说的与我的案例和SQLAlchemy最相关的答案。仍然要感谢LeoK指出正确的方向。
如果您这样重写代码:
from sqlalchemy import as_declarative
@as_declarative
class Model(object):
# ...
def feature():
pass
class Thing(Model):
pass
t = Thing()
t.feature() # No more complain !
这将产生与以前完全相同的Model
类,但不需要中间 CustomBaseModel
类。
由于期望类装饰器返回类,因此在Pylint的意图中这一点更加清楚。它不再失去对类中属性的跟踪。
请注意,没有什么可以阻止您将类与装饰器完全弄混。尽管Pylint可以应付其中的某些问题,但并不容易上当。我想大多数元编程都是这样。
以下是一些示例:
def class_breaker(cls):
# Try some of those:
# E: 37, 0: Assigning to function call which only returns None (assignment-from-none)
# return None # return None too obvious
# E: 47,21: BrokenClass is not callable (not-callable)
# cls = None # Confuses Pylint a bit. Hard to reconcile the message with the issue (IMHO) but correct.
# return cls
# No warnings ! return value is a type
cls = type('Broken', (cls, ), {})
return cls
@class_breaker
class ClassToBreak(object):
def __init__(self, name):
self._name = name
@property
def name(self):
"""Retrieve the name"""
return self._name
class OtherClassToBreak(object):
def __init__(self, name):
"""Init."""
self._name = name
@property
def name(self):
"""Retrieve the name"""
return self._name
BrokenClass = class_breaker(OtherClassToBreak)
def main():
instance = ClassToBreak(name='foo')
print instance.name
other_instance = BrokenClass(name='foo')
print other_instance.name
if __name__ == '__main__':
main()