随机替换Python字典中的两个值
大家好,
首先,我在这个网站上看到很多帖子讨论类似的问题,但没有一个提供有效的解决方案。如果你能在推荐其他帖子之前,先看看我的问题,我将非常感激。
我花了几个小时想找个简单的方法来替换字典中的两个项目,具体来说,就是从两个随机选中的键对中取出项目并互换,比如:
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 个回答
你可能想用 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]
你需要找出与 letter1
和 letter2
匹配的值。如果你希望 letter1
和 letter2
从 dictionary1
中获取值,你应该使用
letter1, letter2 = random.sample(copy.values(), 2)
再看看你的代码,我觉得你的 keys_with_values
函数完全没有必要。只需找到两个随机键并交换它们的值,可以这样做:
key1, key2 = random.sample(copy, 2)
copy[key1], copy[key2] = copy[key2], copy[key1]
不过,如果你确实想让 letter1
和 letter2
成为键,那么你就是在交换两个键的值,而这些值本身也是键。在这种情况下,你可以使用
key1, key2 = random.sample([k for k, v in copy.items() if v in copy], 2)
copy[key1], copy[key2] = copy[key2], copy[key1]
看起来这两个字典好像存储在同一个地方。但这不可能,对吧?
其实,这正是发生的事情。你这一行:
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
,但代码不应该依赖于此。最好是把你的代码写得像 x
和 y
可能是不同对象一样 - 你不能改变这些对象,所以这永远不会重要。相反,使用像 ==
这样的比较函数 - 这些函数在任何实现细节下都能正常工作。实际上,在对象身份不相同的情况下,数字相等也可能成立,比如 x = 7; y = 7.0; x == y
。
你真正需要担心的只是你是否有其他名字指向你的可变对象,比如:
x = [1, 2]
y = x
y.append(3)
Python变量总是指向对象的引用,但在某些情况下,你可以把对象当作值来处理。它们并不是值,但当你对共享对象进行安全操作时(比如对整数的所有操作),你就不需要担心。
唯一的例外是 None
。None
是一个单例对象 - None
实际上是指向一个单一不可变实例的 NoneType
的引用。但因为比较逻辑可能写得不正确,检查 None
值的正确方式是依赖于这种单例特性,使用 x is None
而不是 x == None
,以避免 x
的相等检查返回错误的结果。我没有检查实现细节,看看是否理论上可能创建第二个 NoneType
的对象,但无论如何,没有任何东西会这样做。
你应该确保在改变一个对象时,要么是希望你的改变影响所有指向这个对象的引用,要么是先做一个拷贝。因为不可能改变像 int
这样的不可变类型,所以你不需要担心 int
、tuple
、frozenset
或其他内置不可变类型。