首页 > 解决方案 > 课堂中的外部字典阅读行为

问题描述

我发现了我无法理解的这种行为。我声明这是对我遇到的问题的简化,因为我脚本中的字典有更多项目。

configuration = {
    "video": {
        "fullscreen": {
            "user": None,
            "default": False
        }
    }
}

class test():

    fullscreen = configuration["video"]["fullscreen"]["user"]

    def __init__(self):
        print(configuration)
        print(configuration["video"]["fullscreen"]["user"])
        print(self.fullscreen)

if __name__ == "__main__":
    configuration["video"]["fullscreen"]["user"] = True
    t = test()

这是结果:

{'video': {'fullscreen': {'user': True, 'default': False}}}
True
None

为什么在第三次打印结果是“无”?

标签: pythonpython-3.7

解决方案


再见,

实际上,到目前为止对您的问题所做的解释并没有真正完全向我阐明您的超过合法问题中的指令执行顺序。我想我完全理解你的意思,这也让我感到困惑

下面的例子会告诉你类属性user_conf[重命名以避免关注错误的点]是在运行之前创建configuration["video"]["fullscreen"]["user"] = "John"main()。换句话说 - 在纯类属性级别 - 它的值是从configuration蓝图中设置的。只有类构造函数 - 在之后调用main- 稍后更新该值

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()

请注意,如果您在 main 中注释值更新并取消注释我添加的这些行:

# correcting global variable blueprint
# configuration["video"]["fullscreen"]["user"] = "John"

就在配置声明之后,您将获得没有任何None您期望找到的输出,因为类属性将由“更正”的蓝图创建。在这种情况下,您将获得:

John
John
{'video': {'fullscreen': {'user': 'John', 'default': False}}}
John
John
Jack

在此处的第 6 点产生这种调整样本的另一种方法:

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()

这将给出:

初始外部配置:{'video': {'fullscreen': {'user': None, 'default': False}}}
内部配置:{'video': {'fullscreen': {'user': 'John' , 'default': False}}}
修改外部配置:{'video': {'fullscreen': {'user': 'John', 'default': False}}}

希望这可以更好地解决您的疑问


在 OP 评论之后编辑:正如我公开声明的那样,我花了一些时间来弄清楚发生了什么。让我们看一下这段代码:

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()

打印它会给你以下输出:

步骤 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 刚刚更新了全局变量:{' video': {'fullscreen': {'user': 'John', 'default': False}}}
第 5 步 -> 构造函数读取更新后的值:{'video': {'fullscreen': {'user': '约翰','默认':假}}}

现在,如果您阅读此答案,您将很容易理解 Python 从上到下执行并且执行一个def块 - 在我们的例子__init__(self)中 - 不会立即执行包含的代码。相反,它会在当前范围内创建一个具有给定名称的函数对象,该对象仅在调用test()from之后才实际输入main(),即仅在您要求从test()类中实例化一个对象之后,这将触发其构造函数

重要提示:在您的情况下,我意识到您正在调用该类test()并且test()是您所调用的main()。你的 main 实际上是在调用一个方法,test(): 所以请替换class test()前面def test()的代码,你会得到一个不同的、更容易理解的执行流程:

步骤 1 -> {'video': {'fullscreen': {'user': None, 'default': False}}}
step 3 -> {'video': {'fullscreen': {'user': None, 'default': False}}}
step 4 -> main 刚刚更新了全局变量:{'video': {'fullscreen': {'user': 'John', 'default': False}}}
step 2 -> {'video': {'fullscreen': {'user': 'John', 'default': False}}}

在第一个之后,print所有 def 块都被跳过,我们进入main(). main()更新全局变量,然后该函数test()将立即对更新的值起作用。当然,这种情况下的构造函数不会被触发[这不再是一个类],这解释了缺少第 5 步

-> 你确定你以这种方式定义和使用你的类是一个好的选择吗?[可能不是] -> 声明as不是
更好吗?test()defclass[我真的这么认为]

祝你有美好的一天


推荐阅读