Skip to main content
 首页 » 编程设计

python之在包级别使用类型注释

2025年05月04日28mq0036

我想使用注解在 IDE 中实现更好的自动完成,也许以后在自动化测试中进行类型测试。但是如何在不引入大量额外代码的情况下为许多方法添加类型注释呢?

例如,假设您有一个使用 Django 框架的大型代码库,包括大约 200 个带有 request 参数的方法;添加类型注释 200 次会使代码膨胀。

我目前的想法是在包级别包含类型注释:在 setup.py(或其他地方)中。我定义了一条规则,“每次使用变量 request 时,它的类型都是 django.http.HttpRequest”。对于名称为 request 的变量指向不同类型的边缘情况,应该进行显式注释,但这种情况很少。

如何实现?是否有其他方法可以实现总体目标?

请您参考如下方法:

如果我的理解是正确的,这听起来像是 stub 文件 的用例,如PEP 484 中所述:

Stub files are files containing type hints that are only for use by the type checker, not at runtime.

stub 文件几乎可以满足您将代码与类型注释分开的要求,从而避免复杂类型提示的膨胀和混淆效果。它们有 .pyi 前缀,如果由 IDE 实现的类型检查器想要符合 PEP 484,则必须始终检查类型检查器是否存在。

它们基本上由带注释的函数签名和包含单个省略号的函数体组成 ...


解决此问题的一种方法是使用辅助函数来写出您的 .pyi,或者至少是其中的大部分。可能有很多方法可以做到这一点,哪种方法最好可能完全是另一个问题。

作为执行此操作的示例,我将编写一种方法来注释 inspect 模块中的每个函数,这些函数具有名为 object 的参数并且没有'以下划线开头。我正在输入这个特定的模块,因为我还将使用它来识别函数并获取它们的签名。

在 inspect 模块中,我将使用 getmembers , isfunction和 signature功能。

# gets members of inspect module {member_name: member_type} dict. 
members = getmembers(inspect)   
 
# loop through members 
for name, type in members: 
 
    # grab functions that don't start with an underscore   
    if isfunction(type) and not name.startswith('_'): 
 
        # grab its signature and 
        # check if it has a parameter named object 
        sig = signature(type)   
        if 'object' in sig.parameters: 
 
            # add the annotation to the object parameter 
            param = sig.parameter['object'] 
            s = sig.replace(parameters = [param.replace(annotation="object")]) 
 
            # here you normally write to .pyi file 
            print('def {0} {1}: ...'.format(name, s))   

此逻辑可以扩展到方法、其他参数类型等。 此外,重要说明,.pyi 文件应与您正在注释的模块同名,因此在本例中它应该是 inspect.pyi 。

现在,这只是打印出所有具有名为 object 的参数的函数,以及我们提供的“注释”(object):

def findsource(object:'object'): ... 
def formatannotationrelativeto(object:'object'): ... 
def getabsfile(object:'object'): ... 
def getcomments(object:'object'): ... 
def getdoc(object:'object'): ... 
def getfile(object:'object'): ... 
def getmembers(object:'object'): ... 
def getmodule(object:'object'): ... 
def getsource(object:'object'): ... 
# .. and so on.. 

大多数生产就绪检查器将读取此 .pyi 文件并提供您需要的功能。