考虑一下 代码:

a = {...} # a is an dict with arbitrary contents
b = a.copy()
  1. Mutability在命令的键和值中起什么作用?
  2. 我如何确保不反映另一个键的键或值的更改?
  3. 这与近距离有何关系 约束 dict键?
  4. Python 2.x和Python 3.x之间的行为有任何差异吗?

如何检查python中的类型是否可变?

有帮助吗?

解决方案

1)键不能变形, 除非 您有一个用户定义的类,可覆盖但也可变。这就是强迫你的一切。 但是,使用可伸缩的对象作为dict键可能是一个坏主意。

2)不在两个命令之间共享值。可以共享钥匙,因为它们必须不变。复制字典, copy 模块感,绝对是安全的。在这里调用dict构造函数也有效: b = dict(a). 。您也可以使用不变的值。

3)所有内置不变的类型都是可用的。所有内置可变类型都不可用。为了使对象具有可见,即使它被突变,它在整个生命周期中也必须具有相同的哈希。

4)不是我知道的;我正在描述2.x。

如果类型不变,则可以变形。如果是内置不变的类型,则是不变的: str, int, long, bool, float, tuple, ,也许还有其他几个我忘记的。用户定义的类型总是可变的。

如果物体不变,则可以变异。如果物体仅递归地由不可变性的亚观察值组成,则该对象是不变的。因此,清单元组是可变的。您不能替换元组的元素,但是可以通过列表接口进行修改,从而更改整体数据。

其他提示

实际上,在Python的语言层面上没有任何类似的东西。有些物体无法更改它们(例如,字符串和元素),因此 有效 不变,但这纯粹是概念上的;语言层面上没有属性,既不对您的代码也没有python本身。

不变性实际上与命令无关;将可变值用作钥匙是完全可以的。重要的是比较和哈希:对象必须始终保持与自身的平等。例如:

class example(object):
    def __init__(self, a):
        self.value = a
    def __eq__(self, rhs):
        return self.value == rhs.value
    def __hash__(self):
        return hash(self.value)

a = example(1)
d = {a: "first"}
a.data = 2
print d[example(1)]

这里, example不是 不变;我们正在修改它 a.data = 2. 。但是,我们将其用作哈希的关键,没有任何麻烦。为什么?我们要更改的属性对平等没有影响:哈希没有变化,并且 example(1) 总是等于 example(1), ,忽略任何其他属性。

最常见的用途是缓存和回忆:在逻辑上没有属性缓存或不改变对象,通常对平等没有影响。

(我要在这里停下来 - 请不要一次问五个问题。)

模块中有mutablesequence,mutableset,mutablepapping 收藏. 。可用于检查预制类型的突变性。

issubclass(TYPE, (MutableSequence, MutableSet, MutableMapping))

如果您想在用户定义的类型上使用它,则该类型必须从其中一种继承或注册为虚拟子类。

class x(MutableSequence):
    ...

或者

class x:
    ...

abc.ABCMeta.register(MutableSequence,x)

真的没有 保证 可见的类型也是不可变的,但至少,正确实施 __hash__ 要求类型是不变的,就其自己的哈希和平等而言。这不会以任何特定的方式执行。

但是,我们都是成年人。实施是不明智的 __hash__ 除非您真的意味着它。粗略地说,这只是说,如果实际上可以将一种类型用作字典键,那么它旨在以这种方式使用。

如果您正在寻找的东西 喜欢 一个命令,但也不可变,然后 namedtuple 从标准库中的内容可能是您最好的选择。诚然,这不是一个很好的近似值,但这是一个开始。

  1. dict 钥匙 必须是可观的,这意味着他们有一个不变的 哈希 价值。 dict 可能是或可能不可变的;但是,如果它们是可变的,这会影响您的第二个问题。

  2. “钥匙的更改”将不会在两个命令之间反映。不变值的变化也不会反映出字符串。由于对象由ID存储(IE参考)存储,因此将反映对可变对象的更改,例如用户定义的类。

    class T(object):
      def __init__(self, v):
        self.v = v
    
    
    t1 = T(5)
    
    
    d1 = {'a': t1}
    d2 = d1.copy()
    
    
    d2['a'].v = 7
    d1['a'].v   # = 7
    
    
    d2['a'] = T(2)
    d2['a'].v   # = 2
    d1['a'].v   # = 7
    
    
    import copy
    d3 = copy.deepcopy(d2) # perform a "deep copy"
    d3['a'].v = 12
    d3['a'].v   # = 12
    d2['a'].v   # = 2
    
  3. 我认为这是由前两个答案解释的。

  4. 在这方面,我不知道。

一些其他想法:

有两件事要了解,以了解 钥匙: :键必须是 可用 (这意味着他们实施 object.__hash__(self))而且它们也必须“可比”(这意味着他们实施类似 object.__cmp__(self))。文档中的一项重要收入:默认情况下,用户定义的对象的哈希函数返回 id().

考虑此示例:

class K(object):
  def __init__(self, x, y):
     self.x = x
     self.y = y
  def __hash__(self):
     return self.x + self.y

k1 = K(1, 2)
d1 = {k1: 3}
d1[k1] # outputs 3
k1.x = 5
d1[k1] # KeyError!  The key's hash has changed!
k2 = K(2, 1)
d1[k2] # KeyError!  The key's hash is right, but the keys aren't equal.
k1.x = 1
d1[k1] # outputs 3

class NewK(object):
  def __init__(self, x, y):
     self.x = x
     self.y = y
  def __hash__(self):
     return self.x + self.y
  def __cmp__(self, other):
     return self.x - other.x

nk1 = NewK(3, 4)
nd1 = {nk1: 5}
nd1[nk1] # outputs 5
nk2 = NewK(3, 7)
nk1 == nk2 # True!
nd1[nk2] # KeyError! The keys' hashes differ.
hash(nk1) == hash(nk2) # False
nk2.y = 4
nd1[nk2] # outputs 5

# Where this can cause issues:
nd1.keys()[0].x = 5
nd1[nk1] # KeyError! nk1 is no longer in the dict!
id(nd1.keys()[0]) == id(nk1)  # Yikes. True?!
nd1.keys()[0].x = 3
nd1[nk1]  # outputs 5
id(nd1.keys()[0]) == id(nk1)  # True!

更容易理解,dict存储对对象的引用。阅读有关Hashable的部分。诸如字符串之类的东西是不变的,如果您“更改”它们,那么您现在将其更改为“ dict”,现在引用一个新对象。可变的对象可以“就地更改”,因此两个dicts的价值将会改变。

d1 = {1: 'a'}
d2 = d1.copy()
id(d1[1]) == id(d2[1]) # True
d2[1] = 'z'
id(d1[1]) == id(d2[1]) # False

# the examples in section 2 above have more examples of this.

无论如何,这是所有这一切的要点:

  • 为了 钥匙, ,可能不是 可变性, , 反而 可比性和可比性, ,你在乎。
  • 您关心值的值,因为根据定义,可以更改可变对象的值而不更改对其的引用。

我认为没有一种检验这两个要点的一般方法。适用性的测试将取决于您的用例。例如,检查对象是否实现或未实现 __hash__ 和比较(__eq__ 或者 __cmp__) 功能。就像在 __setattr__ 以某种方式确定它是否可变的方法。

dicts是无序的键:值对。钥匙必须是不变的,因此可以使用。为了确定对象是否可以使用,您可以使用 hash() 功能:

>>> hash(1)
1
>>> hash('a')
12416037344
>>> hash([1,2,3])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> hash((1,2,3))
2528502973977326415
>>> hash({1: 1})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'

另一方面,值可以是任何对象。如果您需要检查对象是否不变,那么我会使用 hash().

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top