3

我是一名长期的 Python 开发人员,我真的很喜欢这种语言的动态特性,但我想知道 Python 是否会从可选的静态类型中受益。 能够将静态类型应用于库的 API 是否有益,这样做的缺点是什么?

我很快勾勒出了一个在 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 函数上使用这样的装饰器是否实用?在我看来,在库的特定领域模块的内部工作中不需要类型检查,但是在库与其客户端之间的连接点上,通过应用类型检查的合同设计的简单版本可能是有用的。特别是作为一种强制文档,它清楚地向图书馆的客户说明它的期望和回报。

像这个示例一样,其中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

使用这种技术有什么缺点?我在 pastebin上的幼稚实现的缺点是什么?

我不想要讨论将 Python 转换为静态类型语言的答案,而是关于 API 设计特定的优缺点的想法。(如果您认为这不是问题,请将其移至programmers.stackexchange.com)

4

1 回答 1

8

就个人而言,我不觉得这个想法对 Python 有吸引力。当然,这只是我的观点,但就上下文而言,我会告诉你 Python 和 Haskell 可能是我最喜欢的两种编程语言——我喜欢静态和动态类型谱系两端的语言。

我看到静态类型的主要好处如下:

  1. 一旦编译器接受它,您的代码正确的可能性就会增加;如果我知道我已经通过我调用的所有操作对我的值进行了线程化,使得一个结果类型始终与另一个输入类型相匹配,并且最终结果类型是我想要的,它增加了我的概率'已经选择了正确的操作。这一点非常值得商榷,因为只有在你没有进行太多测试时才真正重要,这很糟糕。但确实,当我在 Haskell 中编程时,当我坐下来说“那里,完成了!” 我实际上已经完成了很多时间,而我的 Python 代码几乎从来都不是这样。
  2. 当我对数据结构或接口进行不兼容的更改时(大部分时间),编译器会自动指出大部分需要更改的地方。同样,仍然需要进行测试以确保您确实掌握了所有含义,但是根据我的经验,大多数时候编译器的唠叨实际上就足够了,这大大简化了这种重构。您可以直接从实现重构的核心开始测试程序是否仍然可以正常工作,因为进行所有后续更改的实际工作几乎是机械的。
  3. 高效执行。编译器可以使用它所拥有的关于类型的所有知识来进行优化。

您建议的系统并没有真正提供任何这些好处。

  1. 编写了一个使用您的库的程序后,我仍然不知道它是否包含对您的函数的任何类型不正确的使用,直到我使用完整的代码覆盖率进行广泛的测试以查看是否任何执行路径包含错误的调用。
  2. 当我重构某些东西时,我需要经历很多轮“运行完整的测试套件,查找异常,找到它的来源,修复代码”来获得任何东西,比如静态类型编译器的问题检测。
  3. Python 仍然会表现得好像这些变量在任何时候都可以是任何东西。

为了得到那么多,你已经牺牲了 Python 鸭子类型的灵活性;我提供一个足够“类似列表”的对象是不够的,我必须实际提供一个列表。

对我来说,这种静态类型是两全其美的。主要的动态类型参数是“无论如何你都必须测试你的代码,所以你也可以使用这些测试来捕获类型错误,并在它对你没有帮助时让你自己不必在类型系统周围工作”。对于一个非常好的静态类型系统,这可能是也可能不是一个好的论点,但对于仅在运行时检测类型错误的弱部分静态类型系统来说,它绝对是一个令人信服的论点。我不认为更好的错误消息(这是它在大多数情况下真正为你买的;在接口处未捕获的类型错误几乎肯定会在调用堆栈中引发更深的异常)值得失去灵活性。

于 2012-01-28T23:42:38.450 回答