class 中的外部词典阅读行为

External dictionary reading behavior in a class

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

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

为什么第三次打印的结果是"None"?

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

一旦你创建它,它就会进入 class' __dict__ 所以在外部范围中改变一个显然与存储在 class 中的 on 没有关联。

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

再见,

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

以下示例将向您展示 class 属性 user_conf [重命名以避免聚焦错误点] 是在 运行 configuration["video"]["fullscreen"]["user"] = "John" 之前创建的 main()。换句话说 - 在纯 class 属性级别 - 它的值是根据 configuration 蓝图设置的。只有 class 构造函数 - 在 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 的输出,因为 class 属性将由 "corrected" 蓝图创建。在这种情况下,您将得到:

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

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

这会给出:

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}}}

希望能更好的解决你的疑惑


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

打印这将得到以下输出:

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}}}

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

重要:在你的情况下,我意识到你正在调用 class test()test() 是你从 main()。您的 main 实际上是在调用一个方法,test():所以请将之前代码中的 class test() 替换为 def test(),您将得到一个不同且更易于理解的执行流程:

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}}}

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

-> 您确定以这种方式定义和使用您的 class 是正确的选择吗? [可能不是]
-> 将 test() 声明为 def 而不是声明为 class 不是更好吗? [我真的这么认为]

祝你有美好的一天