Syncrossus
Syncrossus

Reputation: 626

Class attributes don't have the same value depending on where they're accessed (Python 3)

It seems depending on where they're accessed from, class attributes don't have the same value. I don't understand why that's the case, and any help in understanding this or working around it would be welcome.

Consider the following .py files :

foo.py:

from bar import *


class Foo:
    someAttribute = None

    @classmethod
    def update(cls, value):
        cls.someAttribute = value


def main():
    Foo.update("some value")
    print(Foo.someAttribute)
    bar = Bar()
    bar.showStuff()


if __name__ == '__main__':
    main()

and bar.py:

from foo import *


class Bar:

    def showStuff(self):
        print(Foo.someAttribute)

One would expect and intend the output to be "some value" for both print instructions. Yet my output is the following :

$ python foo.py
some value
None

Upvotes: 3

Views: 84

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1125138

You have two copies of the foo module in memory. One is called __main__, the other is called foo. That's because you run foo as a script, which is then stored as the __main__ module.

So when you run python foo.py the following happens:

  • sys.modules['__main__'] is created to hold your script namespace.
  • from bar import * is run
    • sys.modules['bar'] is created to hold the bar module namespace.
    • from foo import * is run
      • sys.modules['foo'] is created to hold the foo module namespace.
      • from bar import * is run, the sys.modules['bar'] object is found.
      • No names exist in the empty sys.modules['bar'] module, nothing is imported
      • the Foo class and the main function are added to the foo module.
      • the if __name__ == '__main__' is skipped, this is the foo module.
    • The names Foo and main are added to the bar namespace
    • The Bar class is added to the bar namespace
  • The names Foo, main and Bar are added to the __main__ namespace
  • New objects Foo and main are created in the __main__ namespace.
  • The if __name__ == '__main__' block is executed and main() is called.

Either import from __main__ in bar, or use a third python file to be the main script.

Note that if you use from __main__ import *, then you do have a circular import problem. That's because the from __main__ import * won't include anything that has not yet been executed when the from foo import * line runs, see the above break-down.

Upvotes: 7

Related Questions