课堂中的外部字典阅读行为

时间:2018-11-02 23:54:02

标签: python python-3.7

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

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

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

2 个答案:

答案 0 :(得分:1)

之所以会发生此行为,是因为self.fullscreen引用了在全局范围内对类属性进行编辑之前定义的类属性。

一旦创建它,​​它就会进入类的__dict__中,因此在外部范围中更改一个显然不会与存储在类中的on相关。

如果您写了fullscreen = configuration,我觉得您会得到不同的结果,因为它可能指向同一个字典对象。

答案 1 :(得分:1)

Ciao

实际上,到目前为止,对您的问题的解释并没有真正完全使我澄清您在不合法的问题中执行指令的顺序。我想我完全理解您的意思,这也使我感到困惑

以下示例将向您显示在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()

请注意,如果您在主注释中注释了值更新,并取消注释了我添加的以下几行:

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

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

  

约翰
  约翰
  {'视频':{'全屏':{'用户':'约翰','默认':   False}}}
  约翰
  约翰
  杰克

另一种产生此效果的方法是在点here的第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()

这将给出:

  

初始外部配置:{'视频':{'全屏':{'用户':无,   'default':False}}}
内部配置:{'video':   {'fullscreen':{'user':'John','default':False}}}
已修改   外部配置:{'视频':{'全屏':{'用户':'约翰',   '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-> {'视频':{'全屏':{'用户':无,'默认':假}}}   
步骤2-> {'视频':{'全屏':{'用户':无,'默认':   False}}}
步骤3-> {'视频':{'全屏':{'用户':无,   'default':False}}}
步骤4-> main刚刚更新了全局   变量:{'视频':{'全屏':{'用户':'约翰','默认':   False}}}
步骤5->构造函数读取更新后的值:   {'video':{'fullscreen':{'user':'John','default':False}}}

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

重要:在您的情况下,我意识到您正在调用类test(),而test()是您从main()调用的类。您的主要对象实际上是在调用方法test():因此,请在前面的代码中将class test()替换为def test(),您将获得不同且更易理解的执行流程:

  

步骤1-> {'视频':{'全屏':{'用户':无,'默认':假}}}   
步骤3-> {'视频':{'全屏':{'用户':无,'默认':   False}}}
步骤4-> main刚刚更新了全局变量:   {'视频':{'全屏':{'用户':'约翰','默认':假}}}}
  步骤2-> {'视频':{'全屏':{'用户':'约翰','默认':   False}}}

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

->您确定在以这种方式定义和使用您的类时是一个不错的选择吗? [可能不是]
->将test()声明为def而不是将其声明为class更好吗? [我真的是这样]

祝你有美好的一天