Reputation: 205
Please note: this is not a question about how to change a global variable inside a function body. I understand the global
keyword.
My script has a bunch of global configuration variables. I want write a function that shadows one of those global variables in the local namespace (called modified_procedure()
below) and calls another function that refers to the configuration variable. I.e.,
PARAMETER = 1
def procedure():
return PARAMETER * 3
def modified_procedure():
PARAMETER += 1
return procedure()
This fails because PARAMETER
occurs in the body of modified_procedure()
so the interpreter considers it a local variable and doesn't look it up in the global namespace. I'm not trying to change the global variable PARAMETER
; I am trying to shadow it in modified_procedure()
's namespace.
I can think of a couple of inconvenient solutions:
procedure()
global
in modified_procedure()
to modify PARAMETER
and then restore it before returing the result of procedure()
Can I do it by shadowing PARAMETER
? If so, how?
Upvotes: 3
Views: 1955
Reputation: 76927
Slightly hackish, but you can use the approach below to make things work.
First, define the inital method as below:
PARAMETER = 1
def procedure(PARAMETER = PARAMETER):
return PARAMETER * 3
Now, in the second method, you can use the globals()
method and call it with the modified PARAMETER
:
def modified_procedure_v1():
PARAMETER = globals()["PARAMETER"] + 1
return procedure()
You can even call the first method from within a modified method using the updated PARAMETER value in following manner:
def modified_procedure_v2():
PARAMETER = globals()["PARAMETER"] + 1
return procedure(PARAMETER)
And so, the end result becomes:
>>> modified_procedure_v1() #uses global PARAMETER
3
>>> modified_procedure_v2() #uses PARAMETER value local to this method
6
Upvotes: 4
Reputation: 531625
You could (ab)use the mock
library to temporarily change the value of PARAMETER
as recorded in procedure
's global scope:
import mock
def modified_procedure():
with mock.patch.dict(procedure.func_globals, PARAMETER=PARAMETER+1):
return procedure()
In Python 3, where mock
is part of the unittest
package instead of a third-party library, it would be
from unittest import mock
def modified_procedure():
with mock.patch.dict(procedure.__globals__, PARAMETER=PARAMETER+1):
return procedure()
Without using mock
, you could try something like
def modified_procedure():
# Support Python 2 and 3.
try:
d = procedure.__globals__
except AttributeError:
d = procedure.func_globals
# Ensure that procedure()'s globals are restored
# even if it raises an exception.
try:
d['PARAMETER'] += 1
return procedure()
finally:
d['PARAMETER' -= 1
Upvotes: 1
Reputation:
What you're asking for amounts to dynamic scoping. Python, like most languages still used today (notable exceptions being Perl and Emacs Lisp) does not support dynamic scoping at all, opting for lexical scoping.
You could alter the procedure
function object to replace its __globals__
. But this would be even more hacky, and more code to boot. If you can change procedure
to accept a parameter, do that. If it must refer to a global, temporarily altering the global is going to be the least painful and astonishing approach.
Upvotes: 1