我是一个很长时间的Python开发人员,我非常喜欢这种语言的动态特性,但是我想知道Python是否会从可选的静态类型中受益。
能够将静态类型应用于库的API是否有好处,这种方法的缺点是什么?
我很快勾勒出一个decorator,它实现了运行时静态类型检查on pastebin,它的工作方式如下:

# A TypeError will be thrown if the argument "string" is not a "str" and if
# the returned value is not an "int"
@typed(int, string = str)
def getStringLength(string):
    return len(string)

在库的api函数上使用这样的decorator是否实用?在我看来,在库的特定于域的模块的内部工作中不需要类型检查,但是在库和它的客户端之间的连接点上,通过应用类型检查的契约式设计的简单版本可能是有用的。特别是作为一种强制文档,它清楚地向库的客户说明它所期望和返回的内容。
就像这个例子中的addObjectToQueue()isObjectProcessed()公开供客户机使用,processTheQueueAndDoAdvancedStuff()是一个内部库函数。我认为类型检查在面向外部的函数上是有用的,但是如果在内部函数上使用,它只会膨胀并限制python的动态性和有用性。
# some_library_module.py

@typed(int, name = string)
def addObjectToQueue(name):
    return random.randint() # Some object id

def processTheQueueAndDoAdvancedStuff(arg_of_library_specific_type)
    # Function body here

@typed(bool, object_id = int)
def isObjectProcessed(object_id):
    return True

使用这种技术的缺点是什么?
我的天真实现的缺点是什么?
我不想回答如何将python转换为静态类型语言的问题,而是考虑api设计的优缺点。(如果您认为这不是一个问题,请将其转移到programmers.stackexchange.com)

最佳答案

就我个人而言,我觉得这个想法对python没有吸引力。当然,这只是我的看法,但对于上下文,我会告诉您,Python和Haskell可能是我最喜欢的两种编程语言——我喜欢静态和动态类型谱两端的语言。
我认为静态类型的主要好处如下:
一旦编译器接受了你的代码,你的代码正确的可能性就增加了;如果我知道我已经将我的值线程化到我调用的所有操作中,使得一个的结果类型总是与另一个的输入类型相匹配,并且最终的结果类型是我想要的它增加了我选择正确操作的可能性。这一点有很深的争议价值,因为只有当你不进行太多测试时,这才是真正重要的,这将是不好的。但是,当我在Haskell中编程时,当我坐下来说“好了,完成了!”实际上我已经做了很多次了,而我的Python代码几乎从来没有这样过。
当我对数据结构或接口进行不兼容的更改时(大多数情况下),编译器会自动指出需要更改的大多数位置同样,测试仍然需要确保您已经理解了所有的含义,但根据我的经验,编译器的唠叨在大多数情况下实际上已经足够了,这大大简化了重构;您可以直接从实现重构的核心到测试程序仍然可以正常工作,因为实际工作所有的改变都是机械的。
有效实施。编译器可以使用它所拥有的关于类型的所有知识来进行优化。
你建议的系统并没有提供这些好处。
在使用你的库编写了一个程序之后,我仍然不知道它是否包含了对你的函数的任何类型的错误使用,直到我用完整的代码覆盖率做了大量的测试,看看是否有任何执行路径包含了一个错误的调用。
当我重构某个东西时,我需要经历很多轮的“运行完整的测试套件,寻找异常,找到异常的来源,修复代码”才能得到任何东西,比如静态类型编译器的问题检测。
python仍然会表现得好像这些变量在任何时候都可能是任何东西一样。
为了得到这么多,你牺牲了Python鸭打字的灵活性;我提供一个足够的“列表类”对象是不够的,我必须实际提供一个列表。
对我来说,这种静态类型是两个世界中最糟糕的。主要的动态类型参数是“无论如何,您都必须测试代码,所以您也可以使用这些测试来捕获类型错误,并且在类型系统不起作用时,不必在它周围工作”对于一个真正优秀的静态类型系统来说,这可能是一个很好的参数,也可能不是一个很好的参数,但是对于一个只在运行时检测类型错误的弱部分静态类型系统来说,这绝对是一个很有说服力的参数。我不认为更好的错误消息(这是它真正购买你的大部分时间;一个类型错误没有被捕获在接口几乎肯定会抛出一个异常更深的调用堆栈)是值得失去灵活性。

10-07 19:32
查看更多