Modules and variable scopes - python

I'm not an expert at python, so bear with me while I try to understand the nuances of variable scopes.
As a simple example that describes the problem I'm facing, say I have the following three files.
The first file is outside_code.py. Due to certain restrictions I cannot modify this file. It must be taken as is. It contains some code that runs an eval at some point (yes, I know that eval is the spawn of satan but that's a discussion for a later day). For example, let's say that it contains the following lines of code:
def eval_string(x):
return eval(x)
The second file is a set of user defined functions. Let's call it functions.py. It contains some unknown number of function definitions, for example, let's say that functions.py contains one function, defined below:
def foo(x):
print("Your number is {}!".format(x))
Now I write a third file, let's call it main.py. Which contains the following code:
import outside_code
from functions import *
outside_code.eval_string("foo(4)")
I import all of the function definitions from functions.py with a *, so they should be accessible by main.py without needing to do something like functions.foo(). I also import outside_code.py so I can access its core functionality, the code that contains an eval. Finally I call the function in outside_code.py, passing a string that is related to a function defined in functions.py.
In the simplified example, I want the code to print out "Your number is 4!". However, I get an error stating that 'foo' is not defined. This obviously means that the code in outside_code.py cannot access the same foo function that exists in main.py. So somehow I need to make foo accessible to it. Could anyone tell me exactly what the scope of foo currently is, and how I could extend it to cover the space that I actually want to use it in? What is the best way to solve my problem?

You'd have to add those names to the scope of outside_code. If outside_code is a regular Python module, you can do so directly:
import outside_code
import functions
for name in getattr(functions, '__all__', (n for n in vars(functions) if not n[0] == '_')):
setattr(outside_code, name, getattr(functions, name))
This takes all names functions exports (which you'd import with from functions import *) and adds a reference to the corresponding object to outside_code so that eval() inside outside_code.eval_string() can find them.
You could use the ast.parse() function to produce a parse tree from the expression before passing it to eval_function() and then extract all global names from the expression and only add those names to outside_code to limit the damage, so to speak, but you'd still be clobbering the other module namespace to make this work.
Mind you, this is almost as evil as using eval() in the first place, but it's your only choice if you can't tell eval() in that other module to take a namespace parameter. That's because by default, eval() uses the global namespace of the module it is run in as the namespace.
If, however, your eval_string() function actually accepts more parameters, look for a namespace or globals option. If that exists, the function probably looks more like this:
def eval_string(x, namespace=None):
return eval(x, globals=namespace)
after which you could just do:
outside_code.eval_string('foo(4)', vars(functions))
where vars(functions) gives you the namespace of the functions module.

foo has been imported into main.py; its scope is restricted to that file (and to the file where it was originally defined, of course). It does not exist within outside_code.py.
The real eval function accepts locals and globals dicts to allow you to add elements to the namespace of the evaluted code. But you can't do anything if your eval_string doesn't already pass those on.

The relevant documentation: https://docs.python.org/3.5/library/functions.html#eval
eval takes an optional dictionary mapping global names to values
eval('foo(4)', {'foo': foo})
Will do what you need. It is mapping the string 'foo' to the function object foo.
EDIT
Rereading your question, it looks like this won't work for you. My only other thought is to try
eval_str('eval("foo(4)", {"foo": lambda x: print("Your number is {}!".format(x))})')
But that's a very hackish solution and doesn't scale well to functions that don't fit in lambdas.

Related

python imported module dot notation as function parameter input

New to python programming here. I need some help understand why this does not work:
import x.y # x is a package with __init__.py
def func1(x.y.z): # I get a syntax error here
It works when I do this:
import x.y
a = x.y.z
def func1(a):
I've search the web and can't find anything that would answer this somewhat directly.
Thanks.
With def you define new functions which accept some possibly unknown(!) arguments.
So, def sin(x): means "define a function called sin that accepts one argument". Note that this code means that x can be absolutely anything, the function definition doesn't (and cannot) apply any restrictions on its type, value, size, etc.
When you do
a = "hello"
def test(a):
pass
The a in the function definition is merely an argument that doesn't have any relation to any other a you use in your code! You could've called it x, pi, z or whatever as the name doesn't really matter (code readability aside).
When you try to write
def test(x.y.z):
pass
You get a syntax error as there exist restrictions on the variables' and arguments' names that don't allow you to call a variable any name you want. Why? Simply because otherwise you'll get a lot of uncertainty. For example, how to parse this:
# a poorly formatted number literal or a variable definition??
1234hello = "test"
# attempt to access a member of a class (or module) or a variable definition??
x.y.z = 5
# is "yay a variable's name or a poorly formatted string literal??
x = "yay - 5
# the same question as above
f' = df/dx
A function argument is a variable, so the very same restrictions are imposed on it as well.
BTW, take a look at the SO code highlighter going nuts trying to highlight the code above.

how to extract value of a variable defined in a function using inspect module in python

I am trying to access a particular variable that is defined in a function in python module. I was able to get to the variable name(using inspect.getmembers()) function, I was able to extract all variables that are defined in the function of interest in the (co_varnames) attribute. How do I get the value assigned to the variable ? Here is the sample code:-
File 1:- input.py
class foo(self):
def some_fun():
some_var='abc'
File 2:- sample.py
imported_module = importlib.import_module(input)
all_varnames = inspect.getmembers(imported_module)
-->> need value of all_varnames['some_var']
You don't in that context.
Local variables are only present during the execution of the function, that is between it has been called and returned. Your only possibility would be to do that in a function that the function calles (or if it's a generator during iteration).
Anyhow, if you do it in that way it will still be non-valid again after the function has returned.
Thanks for all the comments and suggestions. #Thomas Wagenaar.#skyking.
This is what I finally ended up doing that, I defined the variable like this inside the function:-
def testMethod(self):
self.myvar = {'a':1,'b':2}
Then using a inspect module and getattr(), I could instantiate the class, call this testMethod(which failed with exception), but got to a point where the variable was initialized and then just read the variable. I dont know if the solution is "correct" way of doing things in python, but this little hack worked for me.

global variable defined in main script can't be accessed by a function defined in a different module

I want to define a few variables in my main python script and use them in a function which is defined in a separate module. Here is an example code. Lets say the main script is named main.py and the module is called mod.py.
Mod.py
def fun():
print a
main.py
from mod import *
global a
a=3
fun()
Now, this code gives me an error
NameError: global name 'a' is not defined
Can anyone please explain why the error is generated (i mean, a variable defined as global should be available to all functions, right?) and what may be a work-around? I already know about these two options and don't want to take any of these
Define the variable in the module instead of the main script.
pass the variable as argument to the function.
If there is any other option, please suggest.
Edit
I dont want to take the above options because
currently these values are fixed for me. But I suspect they may change in future (for example, database name and host ip). So, I want to store them as variables in one place. So that it becomes easy to edit the script in future. If I define the variables in each module, I will have to edit all of them.
I don't want to pass them in the functions because there are too many of them, some 50 or so. I know I can pass them as **kwarg, but that doesn't look too nice.
Global variables shared among modules are generally a bad idea. If you need them though (for example for some configuration purposes), you can do it like this:
global_config.py
# define the variable
a = 3
main.py
import global_config
def fun():
# use the variable
print(global_config.a)
This:
a variable defined as global should be available to all functions, right?
is just not true. That's not how global variables work; they are available to all functions in the module where they are defined.
You don't explain what you're doing or why those solutions don't work for you, but generally speaking global variables are a bad idea; passing the value explicitly is almost always the way to go.

Can I use same argument names when passing arguments in Python

Can you please help me guys. I believe I've got pretty easy questions but don't want to stuff up with my assignment. I'm going to have Class in my module, this class will have few functions.
I just want to be sure it works alright and this is a not ugly code practice.
I.e. my first function test_info accepts one parameter test_code and returns something and the second function check_class accepts two parameter, one of them is called test_code as well
Can I use same argument name: test_code? Is it normal code practice?
def test_info (self, test_code):
my_test_code = test_code
#here we'll be using my_test_code to get info from txt file and return other info
def check_class (self, test_code, other_arg):
my_test_code = test_code
#here some code goes
Also is it fine to use my_test_code in both functions to get argument value or is it better to use different ones like my_test_code_g etc.
Many thanks
Yes you may.
The two variables test_code are defined only in the scope of their respective functions and therefore will not interfere with one another since the other functions lie outside their scope.
Same goes for my_test_code
Read online about variable scopes. Here is a good start
There is no technical reason to resolve this one way or another. But if the variables don't serve exactly the same purpose in both functions, it's confusing for a human reader if they have the same name.

add a local variables to a function

is it possible to add a local varible to a function, just before calling it ? if yes how ?
EDIT:REASON
i noticed that all my views in django are using
render_to_response(template_name,locals())
now i created a middleware and i wanted to add one more local variable using the
def process_view():
method of it .so that i don't have to modify the views .
The local scope for a function does not exist until the function is called, so it's not possible to do this. You could do this for a closure, but the next person to have to maintain the code would hunt you down and kill you.
Although I also think it is pretty useless, I thought that you may enclose the function in either a 'with' statement or another function, like the code below. Of course, this approach can be accomplished directly within the function of interest. In fact, you are adding the local variable 'during' the function declaration. See if this fits your needs!
#!/usr/bin/python
def my_funct(_local):
"""My function of interest
"""
print "Local argument was %s" % str(_local)
return "Finished"
def localize(fct, local_var):
"""
"""
return fct(_local = local_var)
## Use function to 'localize' variable
localize(my_funct, local_var="LOCAL_VARIABLE")
## Same effect without supplementary function :
my_funct(_local="LOCAL_VARIABLE")
try:
print local_var
except:
print "No such global variable"
Just some thoughts :)
Cheers
So if you’re one of those lazy
programmers and you like keeping code
particularly concise, you can take
advantage of a built-in Python
function called locals(). It returns a
dictionary mapping all local variable
names to their values, where “local”
means all variables that have been
defined within the current scope.
source
It is a trick in order to not have to explicitly list all of the variables you need to pass in to the function. In this case, you need to explicitly state a variable to pass in. Therefore, you should not be using locals() in the calls you are making in your middle-ware, as the trick was not designed to be used like that.
i mangaged to do that using decorators.

Categories