How to get/set local variables of a function (from outside) in Python?
Question:
If I have a function (in Python 2.5.2) like:
def sample_func():
a = 78
b = range(5)
#c = a + b[2] - x
My questions are:
- How to get the local variables (a,b) of the function from outside without using locals() inside the function? (kind of reflection)
- Is it possible to set a local variable (say x) from outside so that the commented line works? (I know it sounds weird).
Thanks in advance.
EDIT:
Everyone is asking for a use-case. But it is a weird situation. (Don’t blame me, I did not create it). Here is the scenario:
- I have an encrypted python source file containing a python function.
- A C extension module decrypts it and builds that function in-memory.
- A main python program first calls the C extension with that encrypted file location.
- Then the main program calls the function that has been built in-memory (by the C extension)
- But main program needs to know the local variables of that function (Dont ask me why, it was not me)
- For some (damn) reason, main program needs to set a variable too (weirdest of all)
Answers:
I’m not sure what your use-case is, but this may work better as a class. You can define the __call__
method to make a class behave like a function.
e.g.:
>>> class sample_func(object):
... def __init__(self):
... self.a = 78
... self.b = range(5)
... def __call__(self):
... print self.a, self.b, self.x
...
>>> f = sample_func()
>>> print f.a
78
>>> f.x = 3
>>> f()
78 [0, 1, 2, 3, 4] 3
(this is based on your toy example, so the code doesn’t make much sense. If you give more details, we may be able to provide better advice)
The function’s locals change whenever the function is run, so there’s little meaning to access them while the function isn’t running.
No. A function that isn’t being run doesn’t have locals; it’s just a function. Asking how to modify a function’s locals when it’s not running is like asking how to modify a program’s heap when it’s not running.
You can modify constants, though, if you really want to.
def func():
a = 10
print a
co = func.func_code
modified_consts = list(co.co_consts)
for idx, val in enumerate(modified_consts):
if modified_consts[idx] == 10: modified_consts[idx] = 15
modified_consts = tuple(modified_consts)
import types
modified_code = types.CodeType(co.co_argcount, co.co_nlocals, co.co_stacksize, co.co_flags, co.co_code, modified_consts, co.co_names, co.co_varnames, co.co_filename, co.co_name, co.co_firstlineno, co.co_lnotab)
modified_func = types.FunctionType(modified_code, func.func_globals)
# 15:
modified_func()
It’s a hack, because there’s no way to know which constant in co.co_consts is which; this uses a sentinel value to figure it out. Depending on whether you can constrain your use cases enough, that might be enough.
Not sure if this is what you mean, but as functions are objects in Python you can bind variables to a function object and access them from ‘outside’:
def fa():
print 'x value of fa() when entering fa(): %s' % fa.x
print 'y value of fb() when entering fa(): %s' % fb.y
fa.x += fb.y
print 'x value of fa() after calculation in fa(): %s' % fa.x
print 'y value of fb() after calculation in fa(): %s' % fb.y
fa.count +=1
def fb():
print 'y value of fb() when entering fb(): %s' % fb.y
print 'x value of fa() when entering fa(): %s' % fa.x
fb.y += fa.x
print 'y value of fb() after calculation in fb(): %s' % fb.y
print 'x value of fa() after calculation in fb(): %s' % fa.x
print 'From fb() is see fa() has been called %s times' % fa.count
fa.x,fb.y,fa.count = 1,1,1
for i in range(10):
fa()
fb()
Please excuse me if I am terribly wrong… I´m a Python and programming beginner myself…
Expecting a variable in a function to be set by an outside function BEFORE that function is called is such bad design that the only real answer I can recommend is changing the design. A function that expects its internal variables to be set before it is run is useless.
So the real question you have to ask is why does that function expect x to be defined outside the function? Does the original program that function use to belong to set a global variable that function would have had access to? If so, then it might be as easy as suggesting to the original authors of that function that they instead allow x to be passed in as an argument. A simple change in your sample function would make the code work in both situations:
def sample_func(x_local=None):
if not x_local:
x_local = x
a = 78
b = range(5)
c = a + b[2] - x_local
This will allow the function to accept a parameter from your main function the way you want to use it, but it will not break the other program as it will still use the globally defined x if the function is not given any arguments.
If I have a function (in Python 2.5.2) like:
def sample_func():
a = 78
b = range(5)
#c = a + b[2] - x
My questions are:
- How to get the local variables (a,b) of the function from outside without using locals() inside the function? (kind of reflection)
- Is it possible to set a local variable (say x) from outside so that the commented line works? (I know it sounds weird).
Thanks in advance.
EDIT:
Everyone is asking for a use-case. But it is a weird situation. (Don’t blame me, I did not create it). Here is the scenario:
- I have an encrypted python source file containing a python function.
- A C extension module decrypts it and builds that function in-memory.
- A main python program first calls the C extension with that encrypted file location.
- Then the main program calls the function that has been built in-memory (by the C extension)
- But main program needs to know the local variables of that function (Dont ask me why, it was not me)
- For some (damn) reason, main program needs to set a variable too (weirdest of all)
I’m not sure what your use-case is, but this may work better as a class. You can define the __call__
method to make a class behave like a function.
e.g.:
>>> class sample_func(object):
... def __init__(self):
... self.a = 78
... self.b = range(5)
... def __call__(self):
... print self.a, self.b, self.x
...
>>> f = sample_func()
>>> print f.a
78
>>> f.x = 3
>>> f()
78 [0, 1, 2, 3, 4] 3
(this is based on your toy example, so the code doesn’t make much sense. If you give more details, we may be able to provide better advice)
The function’s locals change whenever the function is run, so there’s little meaning to access them while the function isn’t running.
No. A function that isn’t being run doesn’t have locals; it’s just a function. Asking how to modify a function’s locals when it’s not running is like asking how to modify a program’s heap when it’s not running.
You can modify constants, though, if you really want to.
def func():
a = 10
print a
co = func.func_code
modified_consts = list(co.co_consts)
for idx, val in enumerate(modified_consts):
if modified_consts[idx] == 10: modified_consts[idx] = 15
modified_consts = tuple(modified_consts)
import types
modified_code = types.CodeType(co.co_argcount, co.co_nlocals, co.co_stacksize, co.co_flags, co.co_code, modified_consts, co.co_names, co.co_varnames, co.co_filename, co.co_name, co.co_firstlineno, co.co_lnotab)
modified_func = types.FunctionType(modified_code, func.func_globals)
# 15:
modified_func()
It’s a hack, because there’s no way to know which constant in co.co_consts is which; this uses a sentinel value to figure it out. Depending on whether you can constrain your use cases enough, that might be enough.
Not sure if this is what you mean, but as functions are objects in Python you can bind variables to a function object and access them from ‘outside’:
def fa():
print 'x value of fa() when entering fa(): %s' % fa.x
print 'y value of fb() when entering fa(): %s' % fb.y
fa.x += fb.y
print 'x value of fa() after calculation in fa(): %s' % fa.x
print 'y value of fb() after calculation in fa(): %s' % fb.y
fa.count +=1
def fb():
print 'y value of fb() when entering fb(): %s' % fb.y
print 'x value of fa() when entering fa(): %s' % fa.x
fb.y += fa.x
print 'y value of fb() after calculation in fb(): %s' % fb.y
print 'x value of fa() after calculation in fb(): %s' % fa.x
print 'From fb() is see fa() has been called %s times' % fa.count
fa.x,fb.y,fa.count = 1,1,1
for i in range(10):
fa()
fb()
Please excuse me if I am terribly wrong… I´m a Python and programming beginner myself…
Expecting a variable in a function to be set by an outside function BEFORE that function is called is such bad design that the only real answer I can recommend is changing the design. A function that expects its internal variables to be set before it is run is useless.
So the real question you have to ask is why does that function expect x to be defined outside the function? Does the original program that function use to belong to set a global variable that function would have had access to? If so, then it might be as easy as suggesting to the original authors of that function that they instead allow x to be passed in as an argument. A simple change in your sample function would make the code work in both situations:
def sample_func(x_local=None):
if not x_local:
x_local = x
a = 78
b = range(5)
c = a + b[2] - x_local
This will allow the function to accept a parameter from your main function the way you want to use it, but it will not break the other program as it will still use the globally defined x if the function is not given any arguments.