引用 __init__.__kwdefaults__ 与显式编写字典

referencing __init__.__kwdefaults__ vs explicitly writing a dictionary

python 3、编码新手python。 我用默认值构建了一个 class 字典,然后尝试基于该 class 字典构建一个嵌套字典并遇到意外行为:

class User:

    def __init__(self, *, name=None, age=None, hobbies=[]):
        self.name = name
        self.age = age
        self.hobbies = hobbies

counter = 0
class_dict = {}

# building the nested dicts with default values
for num in range(0, 3):
    """
    1. referencing "User.__init__.__kwdefaults__"
    vs writting the actual dict directly into the nested dict
    2. {"name": None, "age": None, "hobbies": []}
    """
    class_dict.update({f"key_{num}": User.__init__.__kwdefaults__})
    # class_dict.update({f"key_{num}": {"name": None, "age": None, "hobbies": []}})
print("Blue print: " + str(class_dict))


# updating values in the nested dicts
for x in range(0, 3):   # simplified loop
    dict_key = "key_" + str(counter)
    try:
        if 1 < 2:   # simplified if check
            class_dict[dict_key]["name"] = "updated" + str(counter)
            print("inside loop: " + str(class_dict))
            counter = counter + 1
    except:
        continue

print("<<< final result: " + str(class_dict) + ">>>")  # end-result
  1. “User.init.kwdefaults”版本将更新循环内正确的嵌套字典键为预期的, 但作为最终结果,所有 3 个嵌套的字典“name”键都将“updated2”存储为值。在循环的最后一次迭代中发生的变化在所有嵌套的字典中都发生了变化。

  2. 实际字典"{"name":None,"age":None,"hobbies":[]}"版本也更新了正确的嵌套字典循环内的键如预期的那样。 然而,嵌套字典 1 中的“name”键的最终结果在嵌套字典 2“upd​​ated1”和嵌套字典 2“upd​​ated2”中存储值“updated0”。

2. 的最终结果是我的目标,我花了一段时间才找到问题所在。我不明白为什么两个版本在循环内的行为相同但最终结果不同。 是否有 dunder/magic 方法来引用 class 字典并获得版本 2。作为最终结果?

简化为最小示例,您的代码归结为:

d = {'name':None}

dicts = {1: d, 2:d}

print(dicts)
# {1: {'name': None}, 2: {'name': None}}

dicts[1]['name'] = 1
dicts[2]['name'] = 2

print(dicts)
# {1: {'name': 2}, 2: {'name': 2}}

这并不奇怪,因为 dicts[1]dicts[2] 是同一个字典 d.

print(d)
# {'name': 2}

请注意,如果您通过创建一些 User(...) 来调用 __init__,而您在问题的代码中从未这样做过,您将遇到 Least Astonishment” and the Mutable Default Argument 问题。

问题是你一遍又一遍地为每个键分配相同的子命令,你可以在你的代码之后通过 运行 检查:

for x in range(0, 2):   # simplified loop
    dict_key = "key_" + str(x)
    dict_key2 = "key_" + str(x+1)
    print(f'subdict of key {x} is subdict of key {x+1}: {class_dict[dict_key] is class_dict[dict_key2]}')

输出为:

subdict of key 0 is subdict of key 1: True
subdict of key 1 is subdict of key 2: True

一个解决方案是使用深拷贝作为:

import copy

class User:

    def __init__(self, *, name=None, age=None, hobbies=[]):
        self.name = name
        self.age = age
        self.hobbies = hobbies

counter = 0
class_dict = {}

# building the nested dicts with default values
for num in range(0, 3):
    """
    1. referencing "User.__init__.__kwdefaults__"
    vs writting the actual dict directly into the nested dict
    2. {"name": None, "age": None, "hobbies": []}
    """
    class_dict.update({f"key_{num}": copy.deepcopy(User.__init__.__kwdefaults__)})
    # class_dict.update({f"key_{num}": {"name": None, "age": None, "hobbies": []}})
print("Blue print: " + str(class_dict))