Nested functions offer a convenient way to organize code and encapsulate functionality within a broader context. However, understanding how variables are handled within nested scopes can often leave developers perplexed.
Consider the following code snippet:
class Cage(object): def __init__(self, animal): self.animal = animal def get_petters(): for animal in ['cow', 'dog', 'cat']: cage = Cage(animal) def pet_function(): print("Mary pets the " + cage.animal + ".") yield (animal, cage.animal)
In this example, a generator function get_petters() iterates through a list of animals, creating a Cage object for each one and yielding a tuple containing the animal's name and a nested function that attempts to access the cage local variable.
Upon executing this code, you might expect to see three different animals being printed, corresponding to the three different instances of the cage variable. However, the output yields only "Mary pets the cat" repeatedly.
The crux of the issue lies in the nature of closure in Python. When nested functions are defined, they capture references to the variables in their enclosing scope. In the provided code, the pet_function is nested within the get_petters() function and thus has access to the cage variable.
However, this reference is not established at the time of function definition. Instead, it occurs at the time of function execution. By the time the nested functions are executed, the cage variable has already been assigned the value 'cat' as it iterates through the list of animals.
To resolve this issue, one can adopt several approaches:
1. Partial Functions:
A partial function is a callable that wraps an existing function and initializes some of its arguments with preset values. In this case, you can use functools.partial() to create a partial pet function that binds the cage variable to the appropriate context:
def pet_function(cage=None): print("Mary pets the " + cage.animal + ".") yield (animal, partial(pet_function, cage=cage))
2. Creating New Scopes:
Another option is to define the pet function within a nested scope that ensures the cage variable is always locally bound to the correct value:
def scoped_cage(cage=None): def pet_function(): print("Mary pets the " + cage.animal + ".") return pet_function yield (animal, partial(pet_function, cage))
3. Default Keyword Parameters:
You can also pass the cage variable as a default keyword argument to the pet function:
def pet_function(cage=cage): print("Mary pets the " + cage.animal + ".") yield (animal, partial(pet_function))
By adhering to these techniques, you can ensure that nested functions operate with the expected local variables, eliminating unexpected side effects and maintaining code clarity.
The above is the detailed content of Why does my nested function in Python only access the last value of a loop variable?. For more information, please follow other related articles on the PHP Chinese website!