- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
>> n = 674039 >>> one1 = 1 >>> one2 = (n ** 9 + 1) % (n ** 9) >-6ren">
我们可以通过这种方式打败小整数实习生(一次计算可以让我们避开缓存层):
>>> n = 674039
>>> one1 = 1
>>> one2 = (n ** 9 + 1) % (n ** 9)
>>> one1 == one2
True
>>> one1 is one2
False
如何打败小串实习生,即看到如下结果:
>>> one1 = "1"
>>> one2 = <???>
>>> type(one2) is str and one1 == one2
True
>>> one1 is one2
False
sys.intern
提到“Interned strings are not immortal”,但是没有关于如何将字符串踢出 intern 或如何创建 str
实例来避免缓存的上下文层。
由于实习是 CPython 实现细节,因此依赖未记录的实现细节的答案是可以的/预期的。
最佳答案
Unicode 仅包含一个字符(值小于 128 或更精确地来自 latin1
)是最复杂的情况,因为这些字符串实际上不是 interned但是(更类似于整数池或与 bytes
的行为相同)是在开始时创建的,并且 are stored in an array只要口译员还活着:
truct _Py_unicode_state {
...
/* Single character Unicode strings in the Latin-1 range are being
shared as well. */
PyObject *latin1[256];
...
/* This dictionary holds all interned unicode strings...
*/
PyObject *interned;
...
};
所以每次创建一个长度为 1 的 unicode 时,如果它在 latin1
数组中,就会查找字符值。例如。在 unicode_decode_utf8
:
/* ASCII is equivalent to the first 128 ordinals in Unicode. */
if (size == 1 && (unsigned char)s[0] < 128) {
if (consumed) {
*consumed = 1;
}
return get_latin1_char((unsigned char)s[0]);
}
如果有一种方法可以在解释器中规避这一点,甚至有人会争论 - 我们谈论的是(性能)错误。
一种可能性是我们自己使用 C-API 填充 unicode 数据。我使用 Cython
来验证概念,但也可以使用 ctypes
来达到同样的效果:
%%cython
cdef extern from *:
"""
PyObject* create_new_unicode(char *ch)
{
PyUnicodeObject *ob = (PyUnicodeObject *)PyUnicode_New(1, 127);
Py_UCS1 *data = PyUnicode_1BYTE_DATA(ob);
data[0]=ch[0]; //fill data without using the unicode_decode_utf8
return (PyObject*)ob;
}
"""
object create_new_unicode(char *ch)
def gen1():
return create_new_unicode(b"1")
值得注意的细节:
PyUnicode_New
不会在 latin1
中查找,因为字符尚未设置。127
作为 maxchar
传递给 PyUnicode_New
。因此,我们可以通过 PyUnicode_1BYTE_DATA
解释数据,这使得操作起来很容易,而无需手动操作。现在:
a,b=gen1(), gen1()
a is b, a == b
# yields (False, True)
随心所欲。
这是一个类似的想法,但使用 ctypes
实现:
from ctypes import POINTER, py_object, c_ssize_t, byref, pythonapi
PyUnicode_New = pythonapi.PyUnicode_New
PyUnicode_New.argtypes = (c_ssize_t, c_ssize_t)
PyUnicode_New.restype = py_object
PyUnicode_CopyCharacters = pythonapi._PyUnicode_FastCopyCharacters
PyUnicode_CopyCharacters.argtypes = (py_object, c_ssize_t, py_object, c_ssize_t, c_ssize_t)
PyUnicode_CopyCharacters.restype = c_ssize_t
def clone(orig):
cloned = PyUnicode_New(1,127)
PyUnicode_CopyCharacters(cloned, 0, orig, 0, 1)
return cloned
值得注意的细节:
PyUnicode_1BYTE_DATA
与 ctypes 一起使用,因为它是一个宏。另一种方法是计算到 data
-member 的偏移量并直接访问此内存(但这取决于平台并且感觉不太便携)PyUnicode_CopyCharacters
(可能还有其他实现相同目的的可能性),这比直接计算/访问内存更抽象和可移植。_PyUnicode_FastCopyCharacters
,因为 PyUnicode_CopyCharacters
会检查目标 unicode 是否有多个引用并抛出。 _PyUnicode_FastCopyCharacters
不执行这些检查并按要求执行。现在:
a="1"
b=clone(a)
a is b, a==b
# yields (False, True)
对于长度超过 1 个字符的字符串,避免驻留要容易得多,例如:
a="12"
b="123"[0:2]
a is b, a == b
#yields (False, True)
关于python - 如何在两个不同的内存位置创建 str "1"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69573499/
我是一名优秀的程序员,十分优秀!