为什么Python lint要我为相同目的使用不同的局部变量名,而不是全局变量名?
给定这样的Python代码:
def func():
for i in range(10):
pass
for i in range(10):
pass
pylint会发出警告
Redefining name 'i' from outer scope
那么,写上面的代码的“Pythonic”方式是什么呢?是不是应该在本地使用不同的变量,比如说j
?
但是为什么呢?因为这两个变量在这两种情况下的意思是完全一样的(i
都是用来表示索引)。假设我把所有本地的索引都改成j
,然后后来我发现我想在全局范围内用j
作为第二个索引。那我还得再改一次吗?
我不能关闭lint的警告,我也不想有这些警告,我想写得更符合Python的风格,但在像上面这样简单的情况下,我又想对同样的东西使用相同的名字。这难道不可能吗?
3 个回答
这样做是为了避免你在以为自己在用一个东西的时候,实际上却在用另一个东西。Lint工具的目的是让你的代码更健壮。通过给所有变量起不同的名字,你可以确保不会出现这种冲突。
这在解释型语言中尤其重要,因为错误不会在“编译时”被检查。我曾经遇到过一个问题,第二次调用一个函数时出错,因为我重命名了一个函数,却没有意识到在某些情况下,有一个变量的名字和我的函数是一样的。因此,当我试图调用我的函数时,解释器却试图“调用”我新创建的变量,这根本行不通,真是搞笑。
这个Lint政策可以避免这种问题。
下面是一个示例代码(这是一个计算π的程序):
from fractions import Fraction
def order(x):
r, old_r, n, old_n = 2, 1, 1, 0
while (x>=r):
r, old_r, n, old_n = r*r, r, 2*n, n
return order(x >> old_n) + old_n if old_n > 0 else 0
def term(m, n, i):
return Fraction(4 * m, n**(2*i+1) * (2*i+1))
def terms_generator(exp_prec):
ws = [ [term(parm[1], parm[2], 0), 0] + list(parm)
for parm in ((1, 44, 57),
(1, 7, 239),
(-1, 12, 682),
(1, 24, 12943))]
digits = 0
while digits<exp_prec:
curws = max(ws, key=lambda col: col[0])
digits = int(0.30103 *
(order(curws[0].denominator))
- order(curws[0].numerator))
yield curws[2] * curws[0], digits
curws[2] = -curws[2]
curws[1] += 1
curws[0] = term(curws[3], curws[4], curws[1])
expected_precision = 1000
pi = 0
for term, dgts in terms_generator(expected_precision):
pi += term
print("{} digits".format(dgts))
print("pi = 3.{}".format(int((pi-3)*10**expected_precision)))
在这个例子中,我从一个生成器初始化了一个变量,而这个生成器使用了另一个函数,这个函数的名字和我初始化的变量冲突。其实,这不是一个很好的例子,因为这里两个名字都是全局的,但从结构上看,这种冲突并不明显。
我的意思是,即使你知道怎么编程,你也会犯错,而这些做法可以帮助减少那些错误被隐藏的风险。
你可以通过不使用全局变量来避免全局变量冲突:
def func():
for i in range(10):
pass
def _init_func():
for i in range(10):
pass
_init_func()
任何需要在模块初始化时运行的代码都可以放在一个单独的函数里。这样,在模块初始化时唯一需要执行的代码就是:def
语句、class
语句和一个函数调用。
同样,如果你的代码不是为了被 import
引入,而是一个要直接运行的脚本,
def func():
for i in range(10):
pass
def main():
for i in range(10):
pass
if __name__=="__main__":
main()
这个检查工具会发出警告,因为如果循环至少执行了一次,i
的值会在循环结束后继续存在。这意味着如果你在没有重新初始化它的情况下使用它,它仍然会保留在最后一次循环中所得到的值。
不过你使用它的方式是没问题的,因为i
会在每次使用前重新初始化。
一个有用的做法是把外部作用域中的所有值都命名为大写字母(ALL_CAPS
)。这样就不会出错了。
这个回答被认为是错误的,具体情况请查看:https://stackoverflow.com/a/25072186