1

i cant seem to make this work.

I have 2 python files, lets say a.py and b.py

in a.py is this:

def foo():
    global name
    name = "foo"
    import b
    b.bar()

if __name__ == "__main__":
    foo()

in b.py this:

import a

def bar():
    name = a.name
    print(name)

I have three different question in relation to this code:

  1. Why do i get the error: AttributeError: 'module' object has no attribute 'name' I know for certain that b.py cant access the variable defined in the function in b.py but how do i solve this?
  2. does global in this case changes anything? if not, why?
  3. i tried doing name = a.foo.name instead of name = a.name but this doesnt do the trick either and gives me: AttributeError: 'function' object has no attribute 'name', is this even practicable in any case and what did i do wrong?

Thanks for taking the time and sorry if this seems obvious to some of you, i'am still getting into this.

Lyux
  • 453
  • 1
  • 10
  • 22
  • Where do you define `name` in `a.py`? – Ozgur Vatansever Dec 13 '15 at 19:59
  • @ozgur yes it's defined in the function foo() in a.py – Lyux Dec 13 '15 at 20:03
  • 2
    variables defined inside a function are bound to function's local scope so they are not visible outside of the function. You need to define `name` somewhere in a.py outside of `foo()`. – Ozgur Vatansever Dec 13 '15 at 20:08
  • @ozgur problem is, later i want to change ´name´ to something else and when i define it outside of the function it will always get reset when importing right? – Lyux Dec 13 '15 at 20:13
  • No actually, after `foo()` is executed once, variable `name` will remain `"foo"` in the current process even if you `import a` again. – Ozgur Vatansever Dec 13 '15 at 20:18
  • @ozgur - `global name` is sufficient to bind `name` in the module's namespace. Lyux's code would work if both `a` and `b` were imported. – tdelaney Dec 13 '15 at 20:25
  • @tdelaney I didn't say it wouldn't work. I said once `foo()` is called, `name` won't get reset when `a.py` is imported repeatedly within the same process. – Ozgur Vatansever Dec 13 '15 at 20:29
  • @ozgar - You said: "You need to define name somewhere in a.py outside of foo()"_ - that's not true, OP used `global` and that is sufficient. _"variable name will remain "foo" in the current process even if you import a again"_ - only half true since `a.py` is in fact loaded twice. – tdelaney Dec 13 '15 at 20:33
  • @ozgur No, the `global` keyword is sufficient to put `name` in the module namespace as soon as `foo` is run. I think we are just spamming the question at this point. Read up on `global` and see how it works. – tdelaney Dec 13 '15 at 20:45

2 Answers2

0

Scripts aren't modules. Its something of a mind bender but when you run python3 a.py, a.py isn't a module (or more properly, its a module named __main__) and it will be reloaded when you import.

Suppose I have c.py

print('hello')
if __name__=="__main__":
    import c

When I run it I get

hello
hello

To illustrate with your example, if I change b.py to

import __main__

def bar():
    name = __main__.name
    print(name)

It works. But only kindof because if somebody imports a you've still got two modules and two copies of the variable.

If you want shared data, they have to be in imported modules, not top level scripts. When I want shared data, I create a separate module that doesn't import anything (or only imports from the standard library) and it is either empty or has defaults for the variables I want to share.

tdelaney
  • 73,364
  • 6
  • 83
  • 116
  • So i guess shared data would work, i created c.py for a test purpose and only wrote name="foo" into it and made it work, but the thing is, if i want to change the "shared" variable name in c.py to let's say "bar" how would i do this from a.py for example? – Lyux Dec 13 '15 at 20:32
  • If I understand your question, its just `c.bar = c.foo`. And if you wanted to get rid of the original variable completely it would be `del c.foo`. – tdelaney Dec 13 '15 at 20:35
  • c.py is only `name="foo"` now, how do i change the variable `name` out of a.py so there is now `name="bar"` in c.py, you may wonder why i ask this, its because later on b.py shall get the variable `name` from c.py with the value `name="bar"` this is what i understand by "shared data" – Lyux Dec 13 '15 at 20:43
  • 2
    Any of the modules can change the variable with `c.name = "bar"`. After that, any module referencing `c.name` gets `"bar"`. – tdelaney Dec 13 '15 at 20:48
  • alright, got it, big thanks for your patience with me! – Lyux Dec 13 '15 at 20:52
0

1, 2) The specific purpose of if __name__ == "__main__": is to contain code that will not run when the file is imported. Since foo() is inside that block and nowhere else, the foo function does not run when import a happens; therefore the global name is not assigned to; therefore the a module does not have a name global; therefore b cannot refer to the nonexistent module global with a.name.

  1. a.foo.name cannot fix the problem because, although a.foo exists (there is a foo function defined in the a module at the top level), it does not have a name attribute. The local variables in a function's code are not part of the function object; they exist only temporarily, while the code is running, and are not inherently part of any object. (Even if you call locals(), that creates a new dict, and assigns the local variables as values.)

It is possible to assign attributes to a function - for example, in a.py:

def foo():
    pass
foo.name = "bar"

And such an attribute can be used from outside, including in another module:

# b.py
import a
print(a.foo.name)

But this is entirely orthogonal to what the function actually does, or to using it for any particular purpose.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153