简体   繁体   English

无法正确修改 Python class 中的字典

[英]Can't properly modify dictionary in Python class

I've incurred in a strange problem while I was trying to modify a list of dictionaries in a class.我在尝试修改 class 中的字典列表时遇到了一个奇怪的问题。 This is the smallest reproducible code that shows the behaviour:这是显示该行为的最小可重现代码:

from itertools import product

class Test():
    def __init__(self, grid):
        self.grid = grid
        self.pc =  [dict(zip(self.grid, v)) for v in product(*self.grid.values())]
        for i in range(0, len(self.pc)):
            self.pc[i]['sim_options']['id'] = i

grid = {
    'k': [5, 10, 15, 20],
    'sim_options': [
        {'name': 'cosine', 'batched': True},
        {'name': 'pearson', 'batched': True}
    ]
}
t = Test(grid)

What I'd expect as output is the following:我对 output 的期望如下:

[{'k': 5, 'sim_options': {'name': 'cosine', 'batched': True, 'id': 0}},
 {'k': 5, 'sim_options': {'name': 'pearson', 'batched': True, 'id': 1}},
 {'k': 10, 'sim_options': {'name': 'cosine', 'batched': True, 'id': 2}},
 {'k': 10, 'sim_options': {'name': 'pearson', 'batched': True, 'id': 3}},
 {'k': 15, 'sim_options': {'name': 'cosine', 'batched': True, 'id': 4}},
 {'k': 15, 'sim_options': {'name': 'pearson', 'batched': True, 'id': 5}},
 {'k': 20, 'sim_options': {'name': 'cosine', 'batched': True, 'id': 6}},
 {'k': 20, 'sim_options': {'name': 'pearson', 'batched': True, 'id': 7}}]

and yet I get:但我得到:

[{'k': 5, 'sim_options': {'name': 'cosine', 'batched': True, 'id': 6}},
 {'k': 5, 'sim_options': {'name': 'pearson', 'batched': True, 'id': 7}},
 {'k': 10, 'sim_options': {'name': 'cosine', 'batched': True, 'id': 6}},
 {'k': 10, 'sim_options': {'name': 'pearson', 'batched': True, 'id': 7}},
 {'k': 15, 'sim_options': {'name': 'cosine', 'batched': True, 'id': 6}},
 {'k': 15, 'sim_options': {'name': 'pearson', 'batched': True, 'id': 7}},
 {'k': 20, 'sim_options': {'name': 'cosine', 'batched': True, 'id': 6}},
 {'k': 20, 'sim_options': {'name': 'pearson', 'batched': True, 'id': 7}}]

I don't get what I'm doing wrong, am I not iterating over a list, accessing the 'sim_options' field of the i-th list and creating a new key-value pair ('id':i) in that dictionary?我不明白我做错了什么,我没有遍历列表,访问第 i 个列表的'sim_options'字段并在该字典中创建一个新的键值对(“id”:i) ?

Seems like the dictionaries are getting updated by reference, so whatever you do to one, happens to the other.似乎字典正在通过引用进行更新,所以无论你对一个做什么,都会发生在另一个身上。

You can check this with the id() function:您可以使用id() function 进行检查:

for i in range(0, len(self.pc)):
    print(id(self.pc[i]['sim_options']))
    self.pc[i]['sim_options']['id'] = i

Which gives me the same repeating reference identities( 2078935351104 and 2078964975104 ) for both dictiionaries in 'sim_options' :这为'sim_options'中的两个字典提供了相同的重复引用标识( 20789353511042078964975104 ):

2078935351104
2078964975104
2078935351104
2078964975104
2078935351104
2078964975104
2078935351104
2078964975104

One way to get around this is to copy options, which will give you a different reference identity.解决此问题的一种方法是复制选项,这将为您提供不同的参考身份。 I've modified your code slightly to make this possible using copy() .我已经稍微修改了您的代码,以便使用copy()实现这一点。 It also uses enumerate() to loop over the indices and items, which is much nicer to use than range(len(...)) when you need both.它还使用enumerate()来循环索引和项目,当你需要两者时,它比range(len(...))更好用。

from itertools import product
from pprint import pprint

class Test:
    def __init__(self, grid):
        self.grid = grid

        self.pc = []
        for i, (k, options) in enumerate(product(self.grid["k"], self.grid["sim_options"])):
            temp = {"k": k, "sim_options": options.copy()}
            temp["sim_options"]["id"] = i
            self.pc.append(temp)

    def get(self):
        return self.pc

grid = {
    "k": [5, 10, 15, 20],
    "sim_options": [
        {"name": "cosine", "batched": True},
        {"name": "pearson", "batched": True},
    ],
}

t = Test(grid)

pprint(t.get())

Or just build a new list of dictionaries with a list comprehension.或者只是使用列表理解构建一个新的字典列表。 I usually find this way much more preferable.我通常觉得这种方式更可取。

self.pc = [
    {"k": k, "sim_options": {**option, "id": idx}}
    for idx, (k, option) in enumerate(
        product(self.grid["k"], self.grid["sim_options"])
    )
]

Output: Output:

[{'k': 5, 'sim_options': {'batched': True, 'id': 0, 'name': 'cosine'}},
 {'k': 5, 'sim_options': {'batched': True, 'id': 1, 'name': 'pearson'}},
 {'k': 10, 'sim_options': {'batched': True, 'id': 2, 'name': 'cosine'}},
 {'k': 10, 'sim_options': {'batched': True, 'id': 3, 'name': 'pearson'}},
 {'k': 15, 'sim_options': {'batched': True, 'id': 4, 'name': 'cosine'}},
 {'k': 15, 'sim_options': {'batched': True, 'id': 5, 'name': 'pearson'}},
 {'k': 20, 'sim_options': {'batched': True, 'id': 6, 'name': 'cosine'}},
 {'k': 20, 'sim_options': {'batched': True, 'id': 7, 'name': 'pearson'}}]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM