相同的字符串如何在Python 3.5.3中成为不同的对象?

时间:2018-08-02 15:10:41

标签: python string python-3.x immutability

我正在为Flask应用设置pytest。在我的一项测试中,我对返回的JSON数据结构进行了断言。

res = flask_app.get("/api/list_databases") # type: flask.wrappers.Response
assert res.json["status"] is "success"

请注意,status在此上下文中未引用HTTP状态代码。这是应用程序特定的状态属性。

运行测试时,该断言失败。

AssertionError: assert 'success' is 'success'

我知道我在这里使用引用相等测试,这不是严格必要的,但是这个错误让我感到非常好奇。如上,这怎么可能?

如果我在两者上都做id(x),则看到它们具有不同的对象ID。它们都是str的实例(使用type(x))。

但是,根据我对Python的(有限的)理解,以下内容适用:

  • 所有字符串均由unicode代码点组成。在它们变成字符串之前(例如,从磁盘或网络读取它们时),它们是bytes,并且必须使用指定的(或默认的?)字符编码进行解析才能成为str实例。
  • 因此,一旦初始化,字符串将由Unicode代码点组成,而Unicode解释器认为它是有用的任何内部形式。这与Ruby不同,在Ruby中字符串与编码元数据一起存在。因此,您可以同时使用ISO 8859-1字符串和UTF-8字符串。
  • 由于以这种方式对字符串进行了“规范化”,因此字符串føøbar在Python解释器中不可能有两个不同的字节表示形式,即使它是从两个不同的文本文件中以不同的编码读取的。
  • 当字节表示形式不能不同时,这意味着这两个字符串由完全相同的字节序列支持。
  • Python中的字符串是不可变的。
  • 因此,Python解释器将不会创建同一字符串的多个实例。相反,新的引用将指向第一个str对象。这是错误的。查看答案。在某些情况下,可以 字符串,但这是CPython优化,不是语言规范的一部分。

经验证据:

Python 3.5.3 (default, Apr 10 2018, 21:11:57)
[GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a = "foobar"
>>> b = "foobar"
>>> id(a)
4487164008
>>> id(b)
4487164008

哪个最终提出了这个问题:

怎么可能有两个具有相同值的字符串对象(不是引用)?

1 个答案:

答案 0 :(得分:2)

这是一个好问题。考虑以下构造

text / html

您的示例仅适用,因为python注意到字符串为interned。 python 语言不能保证这一点,>>> a = 'hello' >>> b = ''.join(['h', 'e', 'l', 'l', 'o']) >>> a == b True >>> a is b False 实现只是做到了这一点。有关为什么以及何时插入这些字符串的问题在该链接中有完整描述。为您的断言使用cpython-并始终在检查对象的相等性时使用。