随机替换Python字典中的两个值

1 投票
2 回答
734 浏览
提问于 2025-04-18 06:36

大家好,

首先,我在这个网站上看到很多帖子讨论类似的问题,但没有一个提供有效的解决方案。如果你能在推荐其他帖子之前,先看看我的问题,我将非常感激。

我花了几个小时想找个简单的方法来替换字典中的两个项目,具体来说,就是从两个随机选中的键对中取出项目并互换,比如:

dictionary1 = {'a':'A', 'b':'B', 'c':'C'}
dictionary2 = {'a':'B', 'b':'A', 'c':'C'}

现在,我明白字典没有索引,这让这个过程有点棘手。所以我写了这个函数:

def get_neighbor (dictionary1 ):
    dictionary1 = dictionary1 
    def keys_with_value(self, value):
        return [k for k, v in list(self.items()) if v == value]
    copy = {}
    copy = dictionary1 
    letter1 = random.choice(list(copy))
    letter2 = random.choice(list(copy))

    key1 =  (keys_with_value(copy,letter1))[0]
    key2 =  (keys_with_value(copy,letter2))[0]
    copy[key1] = letter2
    copy[key2] = letter1
    return copy

如你所见,这个函数接收字典1并创建一个副本。然后,函数重新分配两个随机选择的字母的键和值。

然而,当我查看这个函数时,发现最终的结果影响了原始字典(dictionary1)和副本(dictionary2)

看起来这两个字典好像存储在同一个地方。但这不可能,对吧?

我非常感谢你们(男生和女生)能提供的任何帮助或建议!

2 个回答

1

你可能想用 random.sample 来代替 random.choice。使用 random.sample 时,你可以指定想要多少个项目,并且可以确保不会得到重复的字母。而如果你用 random.choice 两次,就没有这样的保证。当然,如果你不介意偶尔选择到同一个值(也就是没什么变化),那就不需要做任何更改。

下面是如何使用 random.sample 的示例:

letter1, letter2 = random.sample(copy, 2)

另外,当你在决定要交换的键时,

key1 =  (keys_with_value(copy,letter1))[0]
key2 =  (keys_with_value(copy,letter2))[0]

你需要找出与 letter1letter2 匹配的值。如果你希望 letter1letter2dictionary1 中获取值,你应该使用

letter1, letter2 = random.sample(copy.values(), 2)

再看看你的代码,我觉得你的 keys_with_values 函数完全没有必要。只需找到两个随机键并交换它们的值,可以这样做:

key1, key2 = random.sample(copy, 2)
copy[key1], copy[key2] = copy[key2], copy[key1]

不过,如果你确实想让 letter1letter2 成为键,那么你就是在交换两个键的值,而这些值本身也是键。在这种情况下,你可以使用

key1, key2 = random.sample([k for k, v in copy.items() if v in copy], 2)
copy[key1], copy[key2] = copy[key2], copy[key1]
4

看起来这两个字典好像存储在同一个地方。但这不可能,对吧?

其实,这正是发生的事情。你这一行:

copy = dictionary1

copy 成为已经命名为 dictionary1 的对象的另一个名字。它并没有创建一个新的字典来包含 dictionary1 的内容。

在Python中,变量其实是对象的名字。把一个变量赋值给另一个变量只是把名字指向同一个对象,并没有复制这个对象。有很多方法可以做到这一点,比如说,如果你想做一个浅拷贝,可以使用:

copy.update(dictionary1)

或者,正如Red Alert在上面的评论中提到的,copy = dictionary1.copy()。这比声明一个新的空字典然后用 update 更好。

另外,为了更安全,可以使用 copy.deepcopy 函数。根据你这里的例子,使用浅拷贝和深拷贝没有区别,但如果你的字典里面有列表或其他可变对象作为值,浅拷贝会复制对象的引用,而不是新的对象。

接下来是关于可变对象和不可变对象的一些说明,基于评论的讨论:

在Python的实现中(包括我能准确谈论的常见C实现),通常会重用一些不可变对象。例如,下面的C Python代码会导致两个引用指向同一个对象:

x = 7
y = 7

因为 7 是不可变的,所以这样做是安全的。你可以随后把 x 重新绑定到一个不同的对象,比如:

x = 9

这不会影响 7 的共享对象,y 也不会改变。这种行为并不是不可变对象特有的 - x = 9 以同样的方式重新绑定名字,就像 x = [1, 2] 一样。在这两种情况下,y 都不会受到影响。特别的是可变对象的行为 - 像 x = [1, 2]; x.append(3) 这样的代码会改变底层对象。正确的Python实现不会重用可变类的对象。

一般来说,你不需要担心这个实现细节。虽然在C实现的Python中,x = 7; y = 7; x is y 会返回 True,但代码不应该依赖于此。最好是把你的代码写得像 xy 可能是不同对象一样 - 你不能改变这些对象,所以这永远不会重要。相反,使用像 == 这样的比较函数 - 这些函数在任何实现细节下都能正常工作。实际上,在对象身份不相同的情况下,数字相等也可能成立,比如 x = 7; y = 7.0; x == y

你真正需要担心的只是你是否有其他名字指向你的可变对象,比如:

x = [1, 2]
y = x
y.append(3)

Python变量总是指向对象的引用,但在某些情况下,你可以把对象当作值来处理。它们并不是值,但当你对共享对象进行安全操作时(比如对整数的所有操作),你就不需要担心。

唯一的例外是 NoneNone 是一个单例对象 - None 实际上是指向一个单一不可变实例的 NoneType 的引用。但因为比较逻辑可能写得不正确,检查 None 值的正确方式是依赖于这种单例特性,使用 x is None 而不是 x == None,以避免 x 的相等检查返回错误的结果。我没有检查实现细节,看看是否理论上可能创建第二个 NoneType 的对象,但无论如何,没有任何东西会这样做。

你应该确保在改变一个对象时,要么是希望你的改变影响所有指向这个对象的引用,要么是先做一个拷贝。因为不可能改变像 int 这样的不可变类型,所以你不需要担心 inttuplefrozenset 或其他内置不可变类型。

撰写回答