Reputation: 6395
Given Python code such as
def func():
for i in range(10):
pass
for i in range(10):
pass
pylint complains
Redefining name 'i' from outer scope
What is the Pythonic way to write the above? Use a different variable locally, say j
?
But why, when the variable means exactly the same in both cases (i
for index). Let's say I change all local indexes to j
and then later I find I want to use j
as the second index in the glocal scope. Have to change again?
I can't disable lint warnings, I don't want to have them, I want to write Pythonic, and yet I want to use the same name for the same thing throughout, in the simple case like the above. Is this not possible?
Upvotes: 12
Views: 9672
Reputation: 2846
The linter warns because i
lives on after the loop, if it ran at least once. This means that if you were to use it without re-initializing it it would still have the value it had during the last iteration of the loop.
The way you use it is fine since i
will always be reinitialized.
A useful practice could be to name all values in the outer scope in ALL_CAPS
. This way no mistakes could be made.
This answer was rightfully determined to be wrong. Please see : https://stackoverflow.com/a/25072186
Upvotes: 1
Reputation: 1374
Because it eliminate the risk of being using one when you believe you are using the other. Lint tools are made to make your code more robust. By having all your variables having different names, you ensure that no such conflict could arise.
This is especially critical in interpreted language because the errors are not checked at "compile time". I once had the problem that the second call to a function gave me an error, because I renamed a function and I didn't realize that in some case There was a variable which was created with the same name as my function, and so, when I was trying to call my function, the interpreter tried to "call" my newly created variable, which never worked XD.
this lint policy will avoid that kind of problem.
Here is a sample code (this is a program to compute pi) :
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)))
In this case, I initialized a variable from a generator and the generator used another function which conflicted with my variable name once it was initialized by my generator. Well, It's not a very good example because here both names are global, but from it's structure, it wasn't immediately obvious that it would happen.
My point is that even if you KNOW how to program, you make mistakes and those practices will help reduce the risks for those to stay hidden.
Upvotes: 3
Reputation: 168796
You can avoid global variable conflict by not having any global variables:
def func():
for i in range(10):
pass
def _init_func():
for i in range(10):
pass
_init_func()
Any code that needs to run at module-init time can be put into a single function. This leaves, as the only executable code to run during module init: def
statements, class
statements, and one function call.
Similarly, if your code is not intended to be import
ed, but rather is a script to be run,
def func():
for i in range(10):
pass
def main():
for i in range(10):
pass
if __name__=="__main__":
main()
Upvotes: 31