是否需要“使用严格”的Python编译器?

2024-05-20 15:01:25 发布

您现在位置:Python中文网/ 问答频道 /正文

虽然存在static analysis tools for Python,但编译时检查往往与Python所包含的run-time binding philosophy截然相反。可以用静态分析工具包装标准的Python解释器,以强制执行一些类似“use strict”的约束,但是我们没有看到这种东西被广泛采用。

关于Python,是否有什么东西使得“使用严格”行为不必要或特别不受欢迎?

或者,Perl中的“使用严格”行为是否没有必要,尽管它已经被广泛采用?

注:我所说的“必要”是指“实际必要”,不是严格必要的。显然,您可以在不使用“use strict”的情况下编写Perl,但是(从我所看到的)大多数Perl程序员都使用它。

注意:Python解释器包装器不需要要求“使用严格的”类约束——您可以使用类似于“使用严格的”的伪pragma,该伪pragma将被普通解释器忽略。我不是说要添加一个语言级特性。


更新:解释“use strict”在Perl中的作用。(第一段是官方文件的链接。)

“use strict”指令有三个不同的组件,其中只有两个非常有趣:

  • 使用严格变量:静态检查程序中词汇范围的变量用法。(请记住,在Python中,基本上只有global作用域和local作用域)。许多Python linter都会检查这种情况。因为这是他们唯一能做的静态分析,所以linter假设您使用直接的词法范围,并警告您在这个意义上出现错误的事情,直到您告诉他们闭嘴;即

    FOO = 12
    foo += 3
    

    如果你对你的名字空间不感兴趣,这对检查拼写错误很有用。

  • 使用严格引用:防止符号命名空间取消引用。Python最接近的模拟是使用locals()globals()来进行符号绑定和标识符查找。

  • 使用严格的sub:Python中没有真正的模拟。


Tags: foruselinter静态符号空间情况static
3条回答

“Python所信奉的运行时绑定哲学。。。使“使用严格”行为变得不必要[而且]特别不受欢迎”

总结得不错。谢谢。

本质上就是这样。静态分析工具对Python的帮助还不够大。


编辑

“我要求我们反思一下为什么我们不需要它,以及相关的Perl程序员为什么认为他们确实需要它。”

原因正是你已经给出的理由。我们不需要它,因为它没有帮助。很明显,你不喜欢这个答案,但没什么可说的了。编译时或预编译时检查根本没有帮助。

不过,既然你又花时间问了这个问题,我会为你已经给出的答案提供更多的证据。

我写Java几乎和写Python一样多。Java的静态类型检查并不能防止任何逻辑问题;它不利于满足性能需求;它也无助于满足用例。它甚至没有减少单元测试的数量。

虽然静态类型检查确实能发现方法偶尔的误用,但在Python中同样可以很快发现这一点。在Python中,您可以在单元测试时找到它,因为它不会运行。注意:我不是说错误的类型是通过许多聪明的单元测试发现的,我是说大多数错误的类型问题是通过未处理的异常发现的,在这些异常中,事情根本无法运行到足够远的地方来测试断言。

Pythonistas不浪费时间进行静态检查的原因很简单。我们不需要它。它没有任何价值。这是一个没有经济效益的分析水平。这并不能让我更能解决现实中的人们在处理真实数据时遇到的真正问题。

看看与语言(而不是问题域或库)相关的最流行的SO Python问题。

Is there any difference between "foo is None" and "foo == None"?--==is。没有静态检查可以帮助解决这个问题。另外,请参见Is there a difference between `==` and `is` in Python?

What does ** (double star) and * (star) do for parameters?--*x给出一个列表,**x给出一个字典。如果您不知道这一点,当您尝试执行不适合这些类型的操作时,您的程序将立即终止。”如果你的程序从来没有做过“不合适”的事情呢。那你的程序就行了努夫说。

How can I represent an 'Enum' in Python?——这是对某种有限域类型的请求。一个具有类级别值的类几乎可以完成这项工作。”如果有人改变了任务怎么办”。容易建造。重写__set__以引发异常。是的,静态检查可能会发现这一点。不,在实践中,不会有人对枚举常量和变量感到困惑;当他们这样做时,很容易在运行时发现。”如果逻辑永远无法执行怎么办”。好吧,那是糟糕的设计和糟糕的单元测试。抛出编译器错误并放入从未测试过的错误逻辑,并不比动态语言中从未测试过的情况好多少。

Generator Expressions vs. List Comprehension--静态检查无助于解决此问题。

Why does 1+++2 = 3?——静态检查不会发现这一点。尽管编译器进行了所有检查,C语言中的1+++2还是完全合法的。它在Python中与在C中不同,但同样合法。同样令人困惑。

List of lists changes reflected across sublists unexpectedly——这完全是概念性的。静态检查也不能帮助解决这个问题。Java等价物也会编译并表现得很糟糕。

Python确实有一些东西可以更改脚本语法:

from __future__ import print_function

以及其他具有语法含义的功能。只是Python的语法比历史上的Perl更严格、更稳定、定义更明确;在Python中从来没有“strict refs”和“strict subs”禁止的那种东西。

“strict vars”的主要目的是防止输入错误的引用,并避免“my”创建意外全局变量(好吧,用Perl术语包装变量)。这在Python中是不可能发生的,因为裸赋值默认为本地声明,而裸未赋值符号会导致异常。

(仍然存在这样的情况,即用户意外地尝试写入到全局,而没有用“global”语句声明它,从而导致意外的本地错误,或者更常见的是未绑定的本地错误。这一点往往可以很快学会,但这是一个有争议的情况,必须声明你的本地人可以帮助。尽管很少有经验丰富的Python程序员会接受可读性的负担。)

其他不涉及语法的语言和库更改通过warnings系统处理。

好吧,我不是一个很好的python程序员,但我会说答案是肯定的。

任何允许您随时创建具有任何名称的变量的动态语言都可以使用“strict”pragma。

Perl中的Strict vars(Perl中Strict的选项之一,“use Strict”一次将所有变量都打开)要求在使用之前声明所有变量。这意味着该代码:

my $strict_is_good = 'foo';
$strict_iS_good .= 'COMPILE TIME FATAL ERROR';

在编译时生成致命错误。

我不知道如何让Python在编译时拒绝此代码:

strict_is_good = 'foo';
strict_iS_good += 'RUN TIME FATAL ERROR';

您将得到一个运行时异常,即strict_iS_good未定义。但只有在代码被执行的时候。如果您的测试套件没有100%的覆盖率,您可以很容易地发布这个bug。

每当我在一种没有这种行为的语言(例如PHP)中工作时,我都会感到紧张。我不是一个完美的打字员。一个简单但很难发现的错误会导致代码以很难追踪的方式失败。

因此,重申一下,YESPython可以使用“strict”pragma来启用编译时检查,以检查在编译时可以检查的内容。我想不出还有什么可以添加的检查,但是一个更好的Python程序员可能会想到一些。

注意我关注Perl中stict vars的实用效果,并对一些细节进行了润色。如果你真的想知道所有的细节,请看the perldoc for strict

更新:对一些评论的回复

Jason Baker:像pylint这样的静态检查程序很有用。但它们代表了一个额外的步骤,可以而且经常被跳过。在编译器中构建一些基本检查可以保证这些检查的执行是一致的。如果这些检查是由pragma控制的,那么甚至与检查成本有关的反对意见也就没有意义了。

popcnt:我知道python将生成运行时异常。我也这么说了。我主张尽可能进行编译时检查。请重读这篇文章。

mpeters:没有一个代码的计算机分析可以找到所有错误——这等于解决了停止问题。更糟糕的是,要在赋值中找到拼写错误,编译器需要知道您的意图并找到您的意图与代码不同的地方。这显然是不可能的。

但是,这并不意味着不应该进行检查。如果有一类问题很容易被发现,那么就有必要去捕捉它们。

我对pylint和pychecker还不够熟悉,不知道它们会捕获哪些类的错误。正如我所说,我对python非常缺乏经验。

这些静态分析程序很有用。但是,我相信除非它们复制了编译器的功能,否则编译器总是能够比任何静态检查器“了解”更多关于程序的信息。在可能的情况下,不利用这一点来减少错误似乎是浪费。

更新2:

理论上,我同意你的观点,静态分析器可以做任何编译器可以做的验证。对于Python来说,这就足够了。

但是,如果编译器足够复杂(特别是如果有很多改变编译方式的pragma,或者像Perl一样,可以在编译时运行代码),那么静态分析器必须接近编译器/解释器的复杂度才能进行分析。

嘿,所有关于复杂编译器和在编译时运行代码的讨论都显示了我的Perl背景。

我的理解是,Python没有pragmas,不能在编译时运行任意代码。所以,除非我错了或者添加了这些特性,否则静态分析器中相对简单的解析器就足够了。在每次执行时强制执行这些检查肯定会有帮助。当然,我这样做的方式是用一个pragma。

一旦将pragmas添加到组合中,就可以开始dow了在一个不稳定的斜坡上,分析器的复杂性必须与您在pragmas中提供的能力和灵活性成比例地增长。如果你不小心,你可以像Perl一样结束,然后“只有python可以解析python”,我不想看到未来。

也许命令行开关是添加强制静态分析的更好方法;)

(当我说Python不能像Perl那样使用编译时行为时,决不打算指责它的能力。我预感这是一个经过深思熟虑的设计决定,我可以从中看出其中的智慧。Perl在编译时的极端灵活性是,IMHO,这是语言的一大优势和一个可怕的弱点;我也看到了这种方法的智慧。)

相关问题 更多 >