问题描述
编辑
当尝试从作为实例属性的类继承时,会出现此问题。此mcve再现了它,我将以下问题留给后代:
class A:
class SubA:
pass
a = A()
class B(a.SubA):
pass
mypy
输出:
Name 'a.SubA' is not defined
通过:
class A:
class SubA:
pass
class B(A.SubA):
pass
此Related Issue中的示例与Flask-SQLAlchemy
在db
命名空间下提供声明性基类所做的工作几乎完全相同。在此问题中,mypy
维护者断言他们不支持该模式。
我的问题是,关于上述模式,mypy
将不支持它,这是不正确的吗?尤其是在Flask-SQLAlchemy
之类的大型项目中使用它。
此外,Flask-SQLAlchemy
和mypy
的用户在他们的项目中管理此问题的最佳方法是什么?
原始问题
这个问题不是关于缺少Flask-SQLAlchemy
存根的问题。接受之后,我遇到了这个问题。
请帮助我了解以下原因为何无法按预期工作。
在我的环境中,我仅安装了Flask-SQLAlchemy
和mypy
。
我为mypy
配置了ignore_missing_imports = True
。
mypy
经过以下内容:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Widget(db.Model):
id = db.Column(db.Integer,primary_key=True)
显示:
error: Name 'db.Model' is not defined
因此,我尝试对SQLAlchemy
进行子类化,以为Model
提供注释,该注释显示在对象上的__annotations__
中,但对该模块进行了mypy
分析不变:
from flask_sqlalchemy import SQLAlchemy
from flask_sqlalchemy.model import DefaultMeta
class TypedSQLAlchemy(SQLAlchemy):
Model: DefaultMeta
db = TypedSQLAlchemy()
print(db.__annotations__) # {'Model': <class 'flask_sqlalchemy.model.DefaultMeta'>}
class Widget(db.Model):
id = db.Column(db.Integer,primary_key=True)
执行文件时,print(db.__annotations__)
命令显示{'Model': <class 'flask_sqlalchemy.model.DefaultMeta'>}
,但mypy
仍然有相同的错误:
error: Name 'db.Model' is not defined
我希望为db.Model
提供注释可以使该错误消失。
早期编辑
我最初误解了该错误,因为它并不表明Model
上不存在属性db
,它表明名称db.Model
在该名称中不存在。命名空间。但是,为什么将db.Model
当作全名,而不将db
当作本地定义的名称,而将Model
当作属性呢?这与尝试从类变量继承有关吗?
此外,我的注释不正确,应该是:
class TypedSQLAlchemy(SQLAlchemy):
Model: Type[DefaultMeta]
解决方法
您应该使用A.SubA
。
从mypy
的角度来看,我知道不允许通过实例变量访问嵌套类。由于A
的派生类可以覆盖嵌套类,而mypy无法识别这种情况,因此是这样的:
class A:
class SubA:
pass
class C(A):
class SubA:
pass
def foo(a: A):
class B(a.SubA): # What SubA here ?
pass
foo(C())
更新:
对于Flask-SQLAlchemy,在this讨论中建议以下解决方法:
from app import db
from sqlalchemy.ext.declarative import DeclarativeMeta
BaseModel: DeclarativeMeta = db.Model
class MyModel(BaseModel): ...
如果您正在使用flask_sqlalchemy,则可以从
flask_sqlalchemy.model import DefaultMeta
开始使用,而不是DeclarativeMeta
。