12

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?

Mark Galeck
  • 6,155
  • 1
  • 28
  • 55
  • 4
    There's nothing wrong with that code. PyLint sounds rather touchy to me. – TheSoundDefense Aug 01 '14 at 02:49
  • I'm not sure if there is a general answer to this. What are you doing in your module-level loop? Do you need the global `i` variable afterwards? (I can't imagine why you would, but it's not impossible.) An simple solution might be to put your module-level code inside a function and simply call it from module level. – Blckknght Aug 01 '14 at 02:56

3 Answers3

31

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()
Sjon
  • 4,989
  • 6
  • 28
  • 46
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
3

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.

Camion
  • 1,264
  • 9
  • 22
1

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

Community
  • 1
  • 1
OlivierLi
  • 2,798
  • 1
  • 23
  • 30
  • 11
    According to [PEP-8](http://legacy.python.org/dev/peps/pep-0008/#constants) that would give a wrong hint to the programmer reading the code: "Constants are usually defined on a module level and written in all capital letters with underscores separating words." – Matthias Aug 01 '14 at 07:48
  • 1
    Why is this the accepted answer? `ALL_CAPS` is replacing a minor style issue with a significant one. – Mateen Ulhaq Jul 24 '18 at 06:24
  • Thanks for bringing my attention to this question. – OlivierLi Jul 26 '18 at 18:31