tinutomson
tinutomson

Reputation: 329

Understanding the scope of a class variable in Python3

I have a folder structure like this:

.
├── db
│   └── mysqldb1.py
└── helpers
    ├── __pycache__
    └── context.py

helper/context.py contains a Singleton class like:

class Context():
    g = None

    def __init__(self):
        self.a = 'blah'
        self.b = 'blabla'
        self.conn = None

    @classmethod
    def init_g(cls):
        if not cls.g:
             cls.g = cls()

if __name__ == '__main__':
    Context.init_g()
    from db.mysqldb1 import get_conn
    Context.g.conn = get_conn()

In db.mysqldb1.py I have:

from helpers.context import Context

def get_conn():
    a = Context.g.a
    b = Context.g.b
    ... # more logic

When I execute python -m helpers.context from root folder, I get:

Traceback (most recent call last):
  File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/Users/ttomson/Codes/test/helpers/context.py", line 17, in <module>
    Context.g.conn = get_conn()
  File "/Users/ttomson/Codes/test/db/mysqldb1.py", line 4, in get_conn
    a = Context.g.a
AttributeError: 'NoneType' object has no attribute 'a'

The python interpreter complains that Context.g was never initialized. Why is that the case?

if __name__ == '__main__':
    Context.init_g()

Did I not initialize it when I executed the above? Please help me understand this.

Upvotes: 2

Views: 81

Answers (1)

user2357112
user2357112

Reputation: 282043

This is a weird interaction between __main__ and circular imports. The fix is to change your code to avoid circular imports.

When you run python -m helpers.context, Python runs a version of helpers.context as the __main__ module, separate from the regular helpers.context module. This version runs the if __name__ == '__main__': block, and initializes its own version of Context.

db.mysqldb1 tries to import helpers.context, and it gets the regular version of helpers.context. This version never initialized its Context.g, which is why you get the AttributeError.

Upvotes: 4

Related Questions