61

我并没有像我希望的那样真正关注 Python 3 的开发,只是注意到了一些有趣的新语法变化。具体来自这个 SO answer function 参数注释:

def digits(x:'nonnegative number') -> "yields number's digits":
    # ...

对此一无所知,我认为它可以用于在 Python 中实现静态类型!

经过一番搜索,似乎有很多关于 Python 中(完全可选的)静态类型的讨论,例如PEP 3107“向 Python 添加可选静态类型”(和第 2 部分)中提到的那些

..但是,我不清楚这进展了多远。是否有任何静态类型的实现,使用参数注释?是否有任何参数化类型的想法进入 Python 3?

4

5 回答 5

35

感谢您阅读我的代码!

事实上,在 Python 中创建一个通用的注解实施器并不难。这是我的看法:

'''Very simple enforcer of type annotations.

This toy super-decorator can decorate all functions in a given module that have 
annotations so that the type of input and output is enforced; an AssertionError is
raised on mismatch.

This module also has a test function func() which should fail and logging facility 
log which defaults to print. 

Since this is a test module, I cut corners by only checking *keyword* arguments.

'''

import sys

log = print


def func(x:'int' = 0) -> 'str':
    '''An example function that fails type checking.'''
    return x


# For simplicity, I only do keyword args.
def check_type(*args):
    param, value, assert_type = args
    log('Checking {0} = {1} of {2}.'.format(*args))
    if not isinstance(value, assert_type):
        raise AssertionError(
            'Check failed - parameter {0} = {1} not {2}.'
            .format(*args))
    return value

def decorate_func(func):    
    def newf(*args, **kwargs):
        for k, v in kwargs.items():
            check_type(k, v, ann[k])
        return check_type('<return_value>', func(*args, **kwargs), ann['return'])

    ann = {k: eval(v) for k, v in func.__annotations__.items()}
    newf.__doc__ = func.__doc__
    newf.__type_checked = True
    return newf

def decorate_module(module = '__main__'):
    '''Enforces type from annotation for all functions in module.'''
    d = sys.modules[module].__dict__
    for k, f in d.items():
        if getattr(f, '__annotations__', {}) and not getattr(f, '__type_checked', False):
            log('Decorated {0!r}.'.format(f.__name__))
            d[k] = decorate_func(f)


if __name__ == '__main__':
    decorate_module()

    # This will raise AssertionError.
    func(x = 5)

鉴于这种简单性,乍一看这东西不是主流很奇怪。但是,我相信它没有看起来那么有用是有充分理由的。通常,类型检查会有所帮助,因为如果您添加整数和字典,您很可能会犯一些明显的错误(如果您的意思是合理的,那么显式比隐式更好)。

但是在现实生活中,您经常混合使用编译器看到的相同计算机类型但明显不同的人类类型的数量,例如以下代码段包含一个明显的错误:

height = 1.75 # Bob's height in meters.
length = len(sys.modules) # Number of modules imported by program.
area = height * length # What's that supposed to mean???

只要知道变量的“人类类型”height并且length即使在计算机看来它是和的完全合法的乘法,int任何人都应该立即在上面的行中看到一个错误float

关于这个问题的可能解决方案还有很多可以说的,但是强制执行“计算机类型”显然是一个半解决方案,所以,至少在我看来,这比没有解决方案更糟糕。这也是为什么Systems Hungarian是一个糟糕的想法而Apps Hungarian是一个很棒的想法的原因。在Joel Spolsky的信息非常丰富的帖子中还有更多内容。

现在,如果有人要实现某种 Pythonic 第三方库,该库会自动将其人类类型分配给现实世界的数据,然后小心地转换该类型width * height -> area并使用函数注释强制检查,我认为这将是一种类型检查人真的可以用!

于 2009-08-14T07:53:55.743 回答
15

正如 PEP 中提到的,静态类型检查是函数注释可用于的可能应用程序之一,但它们将其留给第三方库来决定如何执行。也就是说,核心python中不会有正式的实现。

就第三方实现而言,有一些片段(例如http://code.activestate.com/recipes/572161/)似乎做得很好。

编辑:

作为说明,我想提一下检查行为比检查类型更可取,因此我认为静态类型检查不是一个好主意。我上面的回答旨在回答这个问题,而不是因为我会以这种方式对自己进行类型检查。

于 2009-08-14T01:53:23.803 回答
14

Python 中的“静态类型”只能在运行时进行类型检查才能实现,这意味着它会减慢应用程序的速度。因此,您不希望将其作为一般性。相反,您需要一些方法来检查它的输入。这可以通过简单的断言轻松完成,或者如果您(错误地)认为您非常需要它,则可以使用装饰器。

还有一种静态类型检查的替代方法,那就是使用面向方面的组件架构,例如 Zope 组件架构。您无需检查类型,而是对其进行调整。所以而不是:

assert isinstance(theobject, myclass)

你来做这件事:

theobject = IMyClass(theobject)

如果该对象已经实现了 IMyClass,则不会发生任何事情。如果没有,则将查找将任何对象包装到 IMyClass 的适配器,并使用该适配器代替该对象。如果未找到适配器,则会出现错误。

这将 Python 的动态性与以特定方式拥有特定类型的愿望相结合。

于 2009-08-14T05:01:33.390 回答
13

这不是直接回答问题,但我发现了一个添加静态类型的 Python 分支:mypy-lang.org,当然不能依赖它,因为它仍然是小事,但很有趣。

于 2013-02-11T15:05:33.347 回答
0

当然,静态类型似乎有点“unpythonic”,我并不总是使用它。但是在某些情况下(例如嵌套类,如在特定领域的语言解析中),它确实可以加快您的开发速度。

然后我更喜欢使用本文中解释的Beartype * . 它带有一个 git repo、测试和解释它可以做什么和不能做什么......我喜欢这个名字;)

* 请不要关注塞西尔关于为什么 Python 不附带包含在这种情况下的电池的咆哮。

于 2016-08-25T12:44:30.970 回答