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

问题描述 投票:0回答:1

编辑

当尝试从作为实例属性的类继承时,就会出现问题。这个 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

相关问题中的示例几乎与

Flask-SQLAlchemy
db
命名空间下提供声明性基类的作用完全相同。在该问题中,
mypy
维护者声称他们不会支持该模式。

我的问题是,上述模式有什么不正确之处,导致

mypy
不支持它?特别是在大型项目(例如
Flask-SQLAlchemy
)使用它的情况下。

此外,

Flask-SQLAlchemy
mypy
的用户在其项目中管理此内容的最佳方式是什么?


原始问题

这个问题与缺少

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

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

在我的环境中,我仅安装了

Flask-SQLAlchemy
mypy

我已将

mypy
配置为
ignore_missing_imports = True

A

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]
python flask-sqlalchemy python-typing mypy
1个回答
9
投票

您应该使用

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,那么您可以使用 from

flask_sqlalchemy.model import DefaultMeta
而不是
DeclarativeMeta

© www.soinside.com 2019 - 2024. All rights reserved.