包含常量的模块的类型提示

Type hint for a module containing constants

我有一个 config.py 文件,其中有一个常量列表,例如:

config.py
NAME = 'John'
AGE = 23

在另一个文件中,我将这个文件作为模块导入,然后将其作为参数传递给其他函数。我使用 ModuleType 作为此参数的类型。

import config
from types import ModuleType
def f1(config: ModuleType) -> None:
    print(config.NAME)

问题是当我运行pyright linter时,报错:

 79:30 - error: Cannot access member "NAME" for type "ModuleType"
    Member "NAME" is unknown (reportGeneralTypeIssues)

输入配置提示以避免这些错误的正确方法是什么?谢谢!

事实证明,所有模块都是 types.ModuleType 的子类型,因此 typing.ModuleType。因此,你想要的是:

def f1(config: config) -> None:
    print(config.NAME)

第一个config只是一个形式参数,第二个是按名称引用模块。 typing 模块实际上会导入成员,一切正常。

这确实让我想知道这有什么意义。如果必须导入模块才能定义函数,那么为什么要将它作为参数传递呢?

到目前为止,处理此问题的最简单和最方便的方法是不注释 config 参数,或将其注释为 Any。您可以提供更具体的注释,但它会变得非常尴尬。

您现有注释的问题在于您的 f1 被注释为将任意模块作为参数,而任意模块可能没有 NAME 属性。 (另外 ModuleTypetypes 中,而不是 typing。) f1 的正确、特定的注释将指定它采用具有 NAME 属性的东西,这您可以使用自定义协议指定 class:

import typing

class HasName(typing.Protocol):
    NAME: str

def f1(config: HasName) -> None:
    print(config.NAME)

但是你必须对你想在 config 中定义的所有内容执行此操作,如果你想在 config 中允许可选的配置定义,它会变得更加尴尬。

此外,如果您现在尝试将 config 作为参数传递给 f1,它 仍然 不会起作用,因为当您传递一个module 作为参数,mypy 将其视为通用模块,不考虑其内容。 (我不知道 pyright 做了什么,但这就是 mypy 处理它的方式。)你必须明确地投射 config:

f1(typing.cast(HasName, config))

这非常尴尬。另外,一旦你有了这个转换,即使 config 没有 NAME 属性,mypy 也不会报告错误,所以你已经这些笨拙的工作一点安全感都没有。