Reputation: 31
EDIT: Alright, I pretty much figured it out (although it's not quite 100% what I wanted, it still works). It's the execfile()
function, I can't believe I didn't know that was there.
I wasn't really sure how to phrase this question (and that's the reason my Google searches have turned up virtually nothing on this)... but here goes. I'm going to give a simplified version of what I'm trying to do. So let's say I have two files, main.py
and extra.py
. The latter looks something like this:
# extra.py
def setfoo(): # sets "foo" to 5 even if it is unassigned
global foo
foo = 5
So pretty straightforward if I run this in the console:
>>> setfoo()
>>> foo
5
Now let's head back over to main.py
. I'll set up an import statement that imports everything from extra.py
:
# main.py
from extra import *
setfoo()
print foo
Everything works fine up until the last line; however, when main.py
tries to access foo
, it doesn't recognize it since foo
is actually stored under the file extra.py
. If I import everything again after I run setfoo()
, foo
will be imported and everything will work fine. Like so:
# main.py
from extra import *
setfoo()
from extra import *
print foo
Alternatively, if I use a standard import
statement instead of a from...import
statement, there is no need to reimport everything, since the data from extra.py
is being directly accessed rather than copied over. So this will work also:
# main.py
import extra
extra.setfoo()
print extra.foo
However, I really don't want to have to type out extra.setfoo()
every time I want to run setfoo()
, nor do I want to reimport extra.py
each time I use said function. I would like to know if there is a workaround to this. Ideally, there would be a way to modify extra.py
so that the code I set up in the original version of main.py
works properly. (EDIT: It seems multiple people have misinterpreted this–I am willing to modify extra.py
, it's main.py
that I don't want to change, aside from the import statement at the top, to get this code to work.) If this isn't possible I would also consider using a different method of importing extra.py
(I'm thinking something similar to PHP's include
and require
statements, in which the imported code is literally just copied and pasted into the file), but I would highly prefer that this modified import statement still be only one line of code, and not very lengthy. So in other words, using file handling plus an exec
statement probably wouldn't be very convenient for this purpose.
Speaking of exec statements, I really don't care much at all how bad the coding practices I use are. I really just need a way to get this to work since the real version of this extra.py
file is something I plan on using in almost all of my projects from now on, and I don't want to take up a lot of extra space in my code each time I import this file. Any help would be appreciated.
EDIT: It seems that a lot of people reading this aren't exactly clear on what I'm trying to accomplish, so here is the real version of my code. It's a syntax hack I put together to achieve "inline variable assignment". Here is the code (which works fine if you don't attempt to import it into another file):
class use_obj(object):
def __rshift__(self, other): return other
def use(**kwargs):
for var in kwargs: exec 'global ' + var + '; ' + var + ' = kwargs[var]'
return use_obj()
The syntax looks like this:
print use(x = 5, y = 8) >> str(x) + " times " + str(y) + " equals " + str(x*y)
The code itself is pretty gross, but I've always wanted a way to perform inline variable assignment since I'm a big fan of inline if statements, list comprehensions, lambdas + reduce()
, etc. The reason I can't simply have the function return the assigned variables is because use(x = 5, y = 8)
is an expression (not a statement) that returns a weird object that I then shove into the code using the >>
operator, and the weird object that the use()
function returned magically disappears due to the way I set up the __rshift__()
function.
I think the code would lose a lot of its beauty and novelty if it were to look like this:
print use(x = 5, y = 8) >> str(extras.x) + " times " + str(extras.y) + " equals " + str(extras.x*extras.y)
Upvotes: 2
Views: 870
Reputation: 771
First how you can import your variable without modifying extra.py, if really want too, You would have to take help of sys module for getting reference to foo in extra module.
import sys
from extra import *
print('1. Foo in globals ? {0}'.format('foo' in globals()))
setfoo()
print('2. Foo in globals ? {0}'.format('foo' in globals()))
# Check if extra has foo in it
print('2. Foo in extra ? {0}'.format(hasattr(sys.modules['extra'], 'foo')))
# Getting foo explicitly from extra module
foo = sys.modules['extra'].foo
print('3. Foo in globals ? {0}'.format('foo' in globals()))
print("Foo={0}".format(foo))
Output:
1. Foo in globals ? False
2. Foo in globals ? False
2. Foo in extra ? True
3. Foo in globals ? True
Foo=5
Update for later usecase : Modifying extra.py which gets importer and updates its global variables,
# extra.py
import sys
def use(**kwargs):
_mod = sys.modules['__main__']
for k, v in kwargs.items():
setattr(_mod, k, v)
Now importing in any file remains same,
#myfile.py
from extra import *
print use(x = 5, y = 8), str(x) + " times " + str(y) + " equals " + str(x*y)
Output:
None 5 times 8 equals 40
None
appears as use function returns nothing.
Note: It would be better if you choose better pythonic solution for your usecase, unless you are trying to have a little fun with python.
Refer for python scope rules: Short Description of the Scoping Rules?
Upvotes: 1
Reputation: 11453
Without knowing details, one possible solution would be to return foo
in extra.py setfoo()
function instead of declaring as a global variable.
Then declare global foo
in main.py and feed in value from external function setfoo()
Here is the setup
#extra.py
def setfoo(): # sets "foo" to 5 even if it is unassigned
#global foo
foo = 5
return foo
#main.py
from extra import setfoo
global foo
foo = setfoo()
print foo
Result:
Python 2.7.9 (default, Dec 10 2014, 12:24:55) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
>>>
5
>>>
EDIT - 1
OK, take-2 at this problem.
I don't endorse it, but if there is a specific need, If you add a variable to the __builtin__
module, it will be accessible as a global from any other module as long as it includes __builtin__
.
#extra.py
import __builtin__
def setfoo(): # sets "foo" to 5 even if it is unassigned
global foo
__builtin__.foo = 5
#main.py
from extra import *
setfoo()
print foo
Output:
>>>
5
>>>
Upvotes: 0
Reputation: 77347
Modules have namespaces which are variable names bound to objects. When you do from extra import *
, you take the objects found in extra
's namespace and bind them to new variables in the new module. If setfoo
has never been called, then extra
doesn't have a variable called foo
and there is nothing to bind in the new module namespace.
Had setfoo
been called, then from extra import *
would have found it. But things can still be funky. Suppose some assignment sets extra.foo
to 42
. Well, the other module namespace doesn't know about that, so in the other module, foo
would still be 5
but extra.foo
would be 42
.
Always keep in mind the difference between an object and the things that may be referencing the object at any given time. Objects have no idea which variables or containers happen to reference them (though they do keep a count of the number of references). If a variable or container is rebound to a different object, it doesn't change the binding of other variables or containers.
Upvotes: 0