Mypy和从作为实例属性的类继承

问题描述

编辑

当尝试从作为实例属性的类继承时,会出现此问题。此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-SQLAlchemydb命名空间下提供声明性基类所做的工作几乎完全相同。在此问题中,mypy维护者断言他们不支持该模式。

我的问题是,关于上述模式,mypy将不支持它,这是不正确的吗?尤其是在Flask-SQLAlchemy之类的大型项目中使用它。

此外,Flask-SQLAlchemymypy的用户在他们的项目中管理此问题的最佳方法是什么?


原始问题

这个问题不是关于缺少Flask-SQLAlchemy存根的问题。接受之后,我遇到了这个问题。

请帮助我了解以下原因为何无法按预期工作。

在我的环境中,我仅安装了Flask-SQLAlchemymypy

我为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

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...