引用__init __.__ kwdefaults__与显式编写字典

问题描述

python 3,是编码和python的新功能。 我用认值构建了一个类字典,然后尝试基于该类字典构建嵌套字典,并遇到了意外的行为:

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,"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 ”版本将按预期更新循环中正确的嵌套dicts键, 但最终结果是所有3个嵌套字典“名称”键将“ updated2”存储为值。在循环的最后一次迭代中更改的内容在所有嵌套dict中都会更改。

  2. 实际字典“ {” name“:无,” age“:无,”爱好“:[]}”版本还会按预期更新循环内正确的嵌套dict键。 但是,嵌套字典1中“名称”键的最终结果在嵌套字典2中将“ updated1”和嵌套2中“ updated2”存储为值“ updated0”。

2的最终结果是我的目标,花了我一段时间才找到问题。我不明白为什么两个版本在循环内的行为相同,但最终结果却不同。 是否有dunder / magic方法引用类字典并获得版本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}

请注意,如果您通过创建一些__init__来调用User(...),而您在问题代码中从未这样做过,那么您会遇到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,"hobbies": []}})
print("Blue print: " + str(class_dict))