【问题标题】:Python 3 and static typingPython 3 和静态类型
【发布时间】:2010-11-19 12:36:42
【问题描述】:

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

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

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

经过一番搜索,似乎有很多关于(完全可选的)Python 中的静态类型的讨论,例如 PEP 3107"Adding Optional Static Typing to Python"(和 part 2)中提到的那些

..但是,我不清楚这进展到什么程度。是否有任何静态类型的实现,使用参数注释? Python 3 中是否有任何参数化类型的想法?

【问题讨论】:

    标签: python python-3.x static-typing


    【解决方案1】:

    感谢阅读我的代码!

    确实,在 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)
    

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

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

    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???
    

    只要知道变量heightlength 的“人类类型”,即使在计算机上看起来完全合法 乘以@,任何人都应该立即看到上面一行中的错误987654327@和float

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

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

    【讨论】:

    • 如果 bob 必须为他导入的每个模块都用红色油漆粉刷一平方米卧室怎么办?
    • 有没有人想过 Python 中的 IDE 中的代码完成必须有多么严格,因为我们不知道名为 a 的变量是什么类型,我们无法弄清楚当用户输入 a. 并点击 ctrl-space...
    • 我不明白你怎么能从“这个解决方案不能解决每个问题”跳到“这个解决方案比没有更糟糕”。如果你真的认为你甚至不会使用 python,因为 python 不是一种完美的语言,因此比没有更糟糕。即使是正确的静态类型语言也不会强制执行“语义类型”,因为这是一个不可能的问题。他们通常甚至不强制执行物理 SI 类型,但我认为这是因为它通常不值得付出努力(但有些系统会这样做)。
    • 他说“这是一个半解决方案所以它比没有解决方案更糟糕”。也许他的意思是“并且”不是“所以”,但正如所写的那样,他是说所有半解决方案都比没有解决方案更糟糕,这显然是错误的。但无论如何,类型错误在大型动态类型项目中并不罕见。而防止错误只是静态类型的好处之一。还有其他重要的好处,即代码完成、重构和静态分析。我同意它增加了复杂性,所以我开始认为 Dart 的可选类型可能不像最初听起来那么疯狂。
    • 静态类型检查的一点,例如在具有 Python 语法的Boo 中,是将运行时检查转换为编译类型检查。不过代码不错。
    【解决方案2】:

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

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

    编辑:

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

    【讨论】:

    • 请注意,提到的 sn-p 强制执行动态类型检查而不是 static 类型检查。
    • @Andrea Zillio:但在给出注释时,IDE 或脚本可能会在执行(静态)之前检查源代码。想想 def f(x: int) -> int 有人试图让 f('test')。
    • @Joschua:遗憾的是,我看不到 IDE 加入任何非官方的解决方案。
    • @sykora 一些静态类型系统检查行为。您只是在区分名义类型和结构类型。检查 OCaml 的对象系统是否有面向静态行为的静态类型系统。
    【解决方案3】:

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

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

    assert isinstance(theobject, myclass)
    

    你这样做:

    theobject = IMyClass(theobject)
    

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

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

    【讨论】:

    • 注解可以被 pylint 等静态分析工具用来执行一些健全性检查,这与代码区域内的静态类型非常相似,其中变量或参数的类型是已知的,并且 API 上存在注解被调用。
    • @LennartRegebro:这是一个绝妙而简单的想法。我在每种语言中都遇到“非空字符串”问题。大多数接受字符串作为参数的方法都没有为空字符串或所有空白字符串做好充分准备。在大多数情况下,这些都是无效值,但很少有程序员愿意检查它。我喜欢你使用委托(伪)类型来应用类型/值约束的想法once:在构造期间。示例:str -> NonEmptyStrText(这意味着非空且并非所有空格)。
    • 是的,但是由于您仍然可以传入一个空字符串,这实际上并不能解决任何问题。除非您在函数中键入 check,否则伪类型实际上不会添加任何内容。
    • 不会像静态类型一样断言减慢您的应用程序吗?另外,我想知道当最快的语言(c、c++、java)都是静态类型时,静态类型会如何减慢 python。
    • @qed:这正是我所说的。如果你想在 Python 这样的动态语言中进行类型检查,这会减慢速度,因为它是在运行时完成的。除非您在紧密的循环中执行此操作,否则这不太可能产生重大影响。但它会更慢,即使它微不足道。真正的静态类型并不慢,因为它是在编译时完成的。
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-07-11
        • 1970-01-01
        • 2012-10-23
        • 2016-09-28
        • 2022-11-15
        • 2012-06-14
        • 2010-10-27
        相关资源
        最近更新 更多