Python Astroid (Pylint) class 通过工厂时如何模拟继承?
Python Astroid (Pylint) How to mimic inheritance when class passes through factory?
在我工作的地方,我们广泛使用 SQLAlchemy。随着时间的推移,我们为满足我们需求的模型开发了一个基础 class。但是,当需要对我们的代码进行 lint 时,我们总是被我们知道可以忽略的警告所淹没。但到目前为止,我们只能通过 generated-members
指令在全球范围内做到这一点,这往往会隐藏问题。
所以我开始想:“我怎么能把它教给 pylint 呢?”
情况如下:
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 模型实际上或多或少是 CustomBaseModel。
因此看起来我应该在调用 declarative_base()
的 return 值上使用 inference_tip
。但我不确定如何进行。看起来 API 随着时间的推移而改变,我不会去任何地方。
我研究的另一种策略是将 CustomBaseModel
上找到的属性复制到模型中。但它不起作用。事实上,对于 Pylint 模型来说,它似乎只是一个名字……它忘记了它是什么,也不知道它是一个 class.
如有任何提示,我们将不胜感激...
如果你替换这个:
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
。
这是与我的案例和 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
class,但不需要 中间人 CustomBaseModel
class.
由于 class 装饰器预期 return class,这在 Pylint 的意图中更加清晰。它不再丢失 class.
上的属性
请注意,没有什么能阻止您将 class 与装饰器完全混为一谈。尽管 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()
在我工作的地方,我们广泛使用 SQLAlchemy。随着时间的推移,我们为满足我们需求的模型开发了一个基础 class。但是,当需要对我们的代码进行 lint 时,我们总是被我们知道可以忽略的警告所淹没。但到目前为止,我们只能通过 generated-members
指令在全球范围内做到这一点,这往往会隐藏问题。
所以我开始想:“我怎么能把它教给 pylint 呢?”
情况如下:
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 模型实际上或多或少是 CustomBaseModel。
因此看起来我应该在调用 declarative_base()
的 return 值上使用 inference_tip
。但我不确定如何进行。看起来 API 随着时间的推移而改变,我不会去任何地方。
我研究的另一种策略是将 CustomBaseModel
上找到的属性复制到模型中。但它不起作用。事实上,对于 Pylint 模型来说,它似乎只是一个名字……它忘记了它是什么,也不知道它是一个 class.
如有任何提示,我们将不胜感激...
如果你替换这个:
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
。
这是与我的案例和 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
class,但不需要 中间人 CustomBaseModel
class.
由于 class 装饰器预期 return class,这在 Pylint 的意图中更加清晰。它不再丢失 class.
上的属性请注意,没有什么能阻止您将 class 与装饰器完全混为一谈。尽管 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()