Pregunta

I understand that the following behavior is not guaranteed:

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

But, is this behavior guaranteed:

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

If so, what is the correct way to implement this behavior in my own (immutable) classes?


EDIT: By "this behavior" I mean "a constructor for a class that should reuse an existing instance":

>>> x = MyClass("abc", 123)
>>> y = MyClass(x)
>>> assert x is y
¿Fue útil?

Solución

Override __new__() to return the passed-in object:

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

Otros consejos

x is y is really checking id(x) is id(y), i.e. do those two references point to the same object. str in CPython will return the same object if it is already a string, so from that perspective the behaviour you describe:

y = str(x)
assert x is y

will work for all x where isinstance(x, str). From the documentation:

For strings, [str] returns the string itself.

I'm not sure I would consider this a guarantee so much an implementation detail, though, and would try to avoid writing code that relied on it.

There are numerous resources on SO and elsewhere on implementing immutable custom classes in Python, so I will not repeat that information here.

Looks like PyObject_Str function is responsible for conversion to str object. If so, here's what it does when it receives str object as argument (v):

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

It just increases the reference count and returns that object without changes - this is why the object in the following example stays the same:

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

This is indeed an implementation detail, therefore it may vary across different Python versions and implementations.

Also you may find interesting answers to my question Python's int function performance.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top