views:

147

answers:

3

How can I make this print "baz" when I run b.py?

a.py

    def foo():
        global bar
        print bar

b.py

    from a import *
    bar = "baz"
    foo()
+3  A: 

You cannot. The globals from the a module are not affected by the b module, even though the b module copies them to its own globals.

from a import * in b.py simply makes foo in b be a reference to the same function object that foo in a is a reference to. It does not copy the function. The function does not suddenly use b's globals when it accesses a global name.

Pass it as an argument instead:

import a
bar = "baz"
a.foo(bar)

Or, if you just want to modify a.bar, you can assign to that. That affects all calls to foo(), though, not just the one from b.

Thomas Wouters
I'd suggest that beginners forget that Python even has a `global` keyword. (I'd also suggest that it be taken out of the language but I presume someone has found some good use for it somewhere)
msw
Who said anything about the `global` keyword? It's a complete no-op in the OP's question.
Thomas Wouters
Which is why its presence is so confusing to beginners from other languages with different concepts of scope.
msw
A: 

This is terribly not in the spirit of python, but...

a.py
 def foo():
    print bar

b.py
 from a import *
 a.bar = "baz"
 foo()

I tend to avoid from <module> import * anymore. Your programming is unlikely to be limited by your typing speed, and the addition of the module name should not be too much to bear. Setting variables in another module is something that I don't particularly care for either. So yeah the better formation would be:

a.py
 def foo(bar):
    print bar

b.py
 import a
 bar = "baz"
 a.foo(bar)

And look, that's even less typing!

dash-tom-bang
`a.bar = "baz"` doesn't work if you only did `from a import *`. `from a import *` won't introduce the name `a`, only the name `foo` (since that's all that's in `a`'s globals.)
Thomas Wouters
A: 

In an example as simple as this, the real answer in most cases is to use function arguments (as was mentioned). But if the point was to test Python scoping rules, then I think I can clarify...

If you're thinking from a C/C++ context then you could easily be confused by how Python imports work. Module imports do not function like #include directives. In C/C++, the compiler will use #include directives as markers for inserting source code from other source/header files into the current source file (it's own local copy of the included source). The result is that when it's compiled, globals would be scoped for that compilation unit which includes all of the #included source. You can do some dirty tricks with order of #includes to make some really hard-to-understand code. :-P

A Python import does not provide a local module instance that shares the importing module's scope. Instead it provides a reference to a singleton module object that is shared in that current execution of the interpreter. The module object is instantiated the first time it's imported. Subsequent imports, whether in the same module or from completely different modules, merely get a reference to this same module object. A "global" is global within its module. From other modules, it will only be accessible via a reference to that module. For example, in a.py, you would need to import b to access the value of "bar".

In your example, you shouldn't do that since you'll have cyclical imports. If you really need to set module-scoped state you could set a module global from the b module: a.bar = bar (sequence dependence hell).

Don't change a.py,

def foo():
    print bar

but change b.py to:

import a
a.bar = "baz"
a.foo()

A better-encapsulated example would have a.py provide a function to set its global state so that b.py could just execute a.set_module_state( "baz" ).

Jeremy Brown