1

I am new with Python and confused regarding the scope of variables which are declared inside loops. I have seen some examples but it is hard for me to understand it in my specific case.

For example, I saw the following code segment here:

with ZipFile(self.archive_name, "r") as archive:
    for id, files in data.items():
        if files:
            print("Creating", id)
            dirpath = os.path.join(directory, id)

            os.mkdir(dirpath)

            for file in files:
                match = pattern.match(filename)
                new = match.group(2)
                new_filename = os.path.join(dirpath, new)

                content = archive.open(file).read()
            with open(new_filename, "wb") as outfile:
                outfile.write(content)

I basically have the above code repeated in very much the same way but with different statements executed inside the loops. These similar code segments are one after the other inside my __main__. My question is: in that repeated code do I need to give new names to the variables for archive, id, file, files, outfile or not? Will there be a clash or something? Are there any good-practice concerns to keep in mind?

Community
  • 1
  • 1
  • Modified my question to add info regarding where these similar code segments reside. –  Apr 09 '16 at 19:39

2 Answers2

1

Assuming this code is in a function then the scope of the variables is to the end of the function. If this code is at module-level then the scope of the variables is the module (aka global) scope.

You don't have to use different names. The following code will merely assign different objects to the archive variable at different times:

with ZipFile(self.archive_name, "r") as archive:
    print(id(archive))

with ZipFile(self.archive_name, "r") as archive:
    print(id(archive))

It's equivalent to this:

archive = ZipFile(self.archive_name, "r")
with archive:
    print(id(archive))
archive = ZipFile(self.archive_name, "r")
with archive:
    print(id(archive))

That is to say, the blocks associated with with statements and loops don't define a scope so far as variables are concerned, they are "just" assignments. You should see two different values printed for the ids of the different objects.

Beware that since your sample code uses id as a variable name, my example using the built-in function id should be used with care!

Are there any good-practice concerns to keep in mind?

At risk of straying into opinionated territory:

  • You rarely use the value of a loop variable outside the loop. So it's usually fine to use the same loop variable again in a new loop later in the function, but in general you should examine all uses of that variable name in the function before using it again, to be sure. For module-level code this is even worse: before adding the second loop you'd need to make sure that no external users of your module are relying on the variable having the value that the first loop left in it.
  • Unless the object is serving the exact same role in the two different places, then although it's safe to re-use a variable later in the function it can still be a little confusing.
  • Obviously before copy-pasting code twice into a function you want to be reasonably sure that in your particular case, repetition (presumably with some changes) really is better than defining another function and calling it twice.
Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • Thank you for your great answer. Just need a clarification; you say 'If this code is at module-level then the scope of the variables is the module (aka global) scope' which is where my code is. So, 'dont have to use different names'. This confuses me. I mean, since it is the global scope, the `id` variable when first-declared, will it not 'hold' its value until the end?? Sorry if am being slow in understanding this :) –  Apr 09 '16 at 19:33
  • 1
    @hask.duke: if this code is at module-level, then the `id` variable will hold the value you give it until the end of the file, or until you assign a new value to it (which is what your second for loop will do). And furthermore, other modules that import your module will be able to access its final value as `your_module_name.id`. Which is one reason to avoid writing much code at module-level. It doesn't really help anyone to see loop variables in auto-generated documentation, calls to `dir()` when they're using your module from the debugger, and that sort of thing. – Steve Jessop Apr 09 '16 at 19:38
  • So, if I understand this correctly, there is no problem with the `id` variable appearing on the second code segment mainly because it is re-initialised? That is, if had called id right after the end of the first code segment without initialising it, it would hold whatever value it got at the end of that first loop of the first code segment?? –  Apr 09 '16 at 19:42
  • 1
    @hask.duke: yes, you can access a loop variable immediately after the end of the loop, and it holds whatever value it had in the last repeat of the loop. Although as a special case, if the loop executed zero times, because `data.items()` returns an empty list or whatever, then the loop variable won't have been assigned to at all and so if that's the first use in the function then it's not defined. – Steve Jessop Apr 09 '16 at 19:46
  • Perfect. Thank you very much for this much needed clarification. –  Apr 09 '16 at 19:47
1

In general, an indented block does not start a new scope. Only modules, classes, and functions define new scopes. (Well, almost. In Python 3, the indices in a list/set/dict comprehension are local to the comprehension.)

In your example, for instance, archive is in scope for the entire module/class/function in which the with statement occurs, as are any variable first assigned to within the body of the with statement. If the with statement is at module scope, then all assignments are to module globals. If it is at the top level of a class definition, they are all class attributes. If it is (as is likely) defined in a function or method declaration, then they are local to that function.

chepner
  • 497,756
  • 71
  • 530
  • 681