Mark Galeck
Mark Galeck

Reputation: 6395

why does Python lint want me to use different local variable name, than a global, for the same purpose

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

Answers (3)

OlivierLi
OlivierLi

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

Camion
Camion

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

Robᵩ
Robᵩ

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 imported, 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

Related Questions