将不可变Python对象传递给其构造函数后的标识

时间:2014-05-06 14:05:37

标签: python immutability

我了解以下行为是not guaranteed

>>> x = "Hello, world!"
>>> y = "Hello, world!"
>>> assert x is y

但是,这种行为是否得到保证:

>>> x = "Hello, world!"
>>> y = str(x)
>>> assert x is y

如果是这样,在我自己的(不可变的)类中实现这种行为的正确方法是什么?


编辑:通过"此行为"我的意思是"一个应该重用现有实例的类的构造函数":

>>> x = MyClass("abc", 123)
>>> y = MyClass(x)
>>> assert x is y

3 个答案:

答案 0 :(得分:1)

x is y实际上正在检查id(x) is id(y),即这两个引用是否指向同一个对象。 CPython中的str将返回相同的对象,如果它已经是一个字符串,那么从那个角度来看你描述的行为:

y = str(x)
assert x is y

适用于所有x isinstance(x, str)。来自the documentation

  

对于字符串,[str]返回字符串本身。

我不确定我会认为这是一个实现细节的保证,但会尽量避免编写依赖它的代码。

在SO和其他地方有很多资源在Python中实现不可变的自定义类,所以我不会在这里重复这些信息。

答案 1 :(得分:1)

看起来PyObject_Str函数负责转换为str对象。如果是这样,这是它接收str对象作为参数(v)时的作用:

    if (PyUnicode_CheckExact(v)) {
#ifndef Py_DEBUG
        if (PyUnicode_READY(v) < 0)
            return NULL;
#endif
        Py_INCREF(v);
        return v;
    }

它只是增加引用计数并返回该对象而不进行更改 - 这就是以下示例中的对象保持不变的原因:

>>> x = 'long string' * 1000
>>> str(x) is x
True

这确实是一个实现细节,因此它可能因不同的Python版本和实现而有所不同。

您也可以找到我的问题Python's int function performance的有趣答案。

答案 2 :(得分:1)

覆盖__new__()以返回传入的对象:

class C(object):
  def __new__(cls, ref):
    if isinstance(ref, C):
      return ref
    else:
      return super(C, cls).__new__(cls)

  def __init__(self, ref):
    if self is not ref:
      self.x = ref

c1=C(7)
c2=C(13)
assert c1 is not c2
c3=C(c1)
assert c1 is c3