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

Sup*_*oot 5 python typechecking flask-sqlalchemy mypy python-typing

编辑

当尝试从作为实例属性的类继承时,就会出现问题。这个 mcve 重现了它,我将把下面问题的其余部分留给后代:

class A:
    class SubA:
        pass


a = A()

class B(a.SubA):
    pass
Run Code Online (Sandbox Code Playgroud)

mypy输出:

Name 'a.SubA' is not defined
Run Code Online (Sandbox Code Playgroud)

这通过:

Name 'a.SubA' is not defined
Run Code Online (Sandbox Code Playgroud)

本相关问题中的示例几乎正是在命名空间Flask-SQLAlchemy下提供声明性基类的内容db。在该问题中,mypy维护者声称他们不会支持该模式。

我的问题是,上述模式有什么不正确以至于mypy不支持它?特别是在大型项目(例如Flask-SQLAlchemy.

此外,用户Flask-SQLAlchemymypy在项目中管理此内容的最佳方式是什么?


原问题

这个问题与缺少存根无关Flask-SQLAlchemy。接受这一点后,我遇到了这个问题。

请帮助我理解为什么以下内容不能按我的预期工作。

在我的环境中我只安装Flask-SQLAlchemymypy

我已经mypy配置了ignore_missing_imports = True.

跳过mypy以下内容:

class A:
    class SubA:
        pass


class B(A.SubA):
    pass
Run Code Online (Sandbox Code Playgroud)

揭示了:

error: Name 'db.Model' is not defined
Run Code Online (Sandbox Code Playgroud)

因此,我尝试子类化SQLAlchemy以提供 的注释Model,该注释显示在__annotations__对象上,但mypy模块的分析不会改变:

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()


class Widget(db.Model):
    id = db.Column(db.Integer, primary_key=True)
Run Code Online (Sandbox Code Playgroud)

当我执行该文件时,print(db.__annotations__)命令显示{'Model': <class 'flask_sqlalchemy.model.DefaultMeta'>},但仍然mypy出现相同的错误:

error: Name 'db.Model' is not defined
Run Code Online (Sandbox Code Playgroud)

我希望提供注释应该db.Model可以使该错误消失。

早期编辑

我最初误解了该错误,因为它并不表明该属性Model不存在于 上db,而是表明该名称db.Model在命名空间中不存在。但为什么它会被视为db.Model全名,而不是db本地定义的名称及其Model属性呢?这与尝试从类变量继承有关吗?

另外,我的注释不正确,应该是:

error: Name 'db.Model' is not defined
Run Code Online (Sandbox Code Playgroud)

ale*_*ame 8

你应该使用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())
Run Code Online (Sandbox Code Playgroud)

更新:

对于 Flask-SQLAlchemy,本次讨论中建议了以下解决方法:

from app import db
from sqlalchemy.ext.declarative import DeclarativeMeta

BaseModel: DeclarativeMeta = db.Model


class MyModel(BaseModel): ...
Run Code Online (Sandbox Code Playgroud)

如果您使用flask_sqlalchemy,那么您可以使用 fromflask_sqlalchemy.model import DefaultMeta而不是DeclarativeMeta