Actually the explanations given so far to your question were not really totally clarifying to me the order of instructions execution in your more-than-legitimate question. I think I perfectly understood what you meant and it puzzled me too
The following example will show you that the class attribute user_conf
[renamed to avoid focusing on the wrong point] is created before running configuration["video"]["fullscreen"]["user"] = "John"
in the main()
. In other words - at pure class attribute level - its value is set from the configuration
blueprint. It will be only the class constructor - called after the main
- to update that value later
configuration = {
"video": {
"fullscreen": {
"user": None,
"default": False
}
}
}
# correcting global variable blueprint
# configuration["video"]["fullscreen"]["user"] = "John"
class test():
print(configuration["video"]["fullscreen"]["user"])
user_conf = configuration["video"]["fullscreen"]["user"]
print(user_conf)
def __init__(self):
# printing modified global variable, all right
print(configuration)
print(configuration["video"]["fullscreen"]["user"])
print(self.user_conf)
self.user_conf = "Jack"
print(self.user_conf)
def main():
# modifying global variable later
# at this point the class attribute user_conf has already been assigned with the old value
configuration["video"]["fullscreen"]["user"] = "John"
test()
if __name__ == '__main__':
main()
Please notice that if you comment the value update in the main and uncomment these lines that I added:
# correcting global variable blueprint
# configuration["video"]["fullscreen"]["user"] = "John"
just after the configuration declaration you will have the output without any None
that you were expecting to find, because the class attribute will be created by a "corrected" blueprint. In this case then you will get:
John
John
{'video': {'fullscreen': {'user': 'John', 'default':
False}}}
John
John
Jack
Another way to produce this tweaking the sample at point 6 here:
def outer():
configuration = {
"video": {
"fullscreen": {
"user": None,
"default": False
}
}
}
print("initial outer configuration:", configuration)
def inner():
nonlocal configuration
'''
configuration = {
"video": {
"fullscreen": {
"user": "John",
"default": False
}
}
}
'''
configuration["video"]["fullscreen"]["user"] = "John"
print("inner configuration:", configuration)
inner()
print("modified outer configuration:", configuration)
outer()
which would give:
initial outer configuration: {'video': {'fullscreen': {'user': None,
'default': False}}}
inner configuration: {'video':
{'fullscreen': {'user': 'John', 'default': False}}}
modified
outer configuration: {'video': {'fullscreen': {'user': 'John',
'default': False}}}
Hope this can solve better your doubt
Edit after the OP comment: as I openly declared it took me some time to figure out what is happening. Let's take this code:
configuration = {
"video": {
"fullscreen": {
"user": None,
"default": False
}
}
}
print("step 1 -> " + str(configuration))
# correcting global variable blueprint
# configuration["video"]["fullscreen"]["user"] = "John"
class test():
print("step 2 -> " + str(configuration))
user_conf = configuration["video"]["fullscreen"]["user"]
def __init__(self):
# printing modified global variable, all right
print("step 5 -> constructor reads the updated value: " + str(configuration))
def main():
# modifying global variable later
# at this point the class attribute user_conf has already been assigned with the old value
print("step 3 -> " + str(configuration))
configuration["video"]["fullscreen"]["user"] = "John"
print("step 4 -> main just updated the global variable: " + str(configuration))
test()
if __name__ == '__main__':
main()
Printing this will give you the following output:
step 1 -> {'video': {'fullscreen': {'user': None, 'default': False}}}
step 2 -> {'video': {'fullscreen': {'user': None, 'default':
False}}}
step 3 -> {'video': {'fullscreen': {'user': None,
'default': False}}}
step 4 -> main just updated the global
variable: {'video': {'fullscreen': {'user': 'John', 'default':
False}}}
step 5 -> constructor reads the updated value:
{'video': {'fullscreen': {'user': 'John', 'default': False}}}
Now, if you read this answer you will easily understand that Python is executed top to bottom and executing a def
block - in our case __init__(self)
- doesn't immediately execute the contained code. Instead it creates a function object with the given name in the current scope which is actually entered only after calling test()
from main()
, i.e. only after you ask to instance an object from the test()
class, which will trigger its constructor
IMPORTANT: in your case I realized that you are calling the class test()
and test()
is what you are calling from main()
. Your main is calling a method actually, test()
: so please replace class test()
with def test()
in the previous code and you will get a different and more understandable execution flow:
step 1 -> {'video': {'fullscreen': {'user': None, 'default': False}}}
step 3 -> {'video': {'fullscreen': {'user': None, 'default':
False}}}
step 4 -> main just updated the global variable:
{'video': {'fullscreen': {'user': 'John', 'default': False}}}
step 2 -> {'video': {'fullscreen': {'user': 'John', 'default':
False}}}
After the first print
all the def blocks are skipped and we enter the main()
. The main()
updates the global variable and then the test()
function would work immediately on the updated value. Of course the constructor in this case would not be triggered [this is not anymore a class] and this explains the lack of step 5
-> are you sure you are making a good choice in defining and using your class in this way? [probably not]
-> isn't it better to declare test()
as def
instead that as class
? [I really think so]