当你
import pandas
,它能做很多事情,包括打电话
UNICODE_setitem
在所有单个ascii字母字符串上,以及在其他地方对单个ascii数字字符串执行类似的操作。
这个numpy函数调用不推荐使用的c api
PyUnicode_AsUnicode
是的。
当您在cpython 3.3+中调用它时,它会缓存
wchar_t *
字符串内部结构的表示
wstr
成员,作为两个wchar_t值
w'a'
和
'\0'
,它在32位上占用8个字节-
wchar_t
python的构建。以及
str.__size__
考虑到这一点。
所以,所有单字符的ascii字母和数字的字符串都被截取了,但没有比这个大8个字节的。
首先,我们知道很明显
导入熊猫
(每
Brad Solomon's answer
)可能发生在
np.set_printoptions(precision=4, threshold=625, edgeitems=10)
(米拉杜洛发表了一条评论,但随后被删除)
ShadowRanger's answer
),但绝对不在
import numpy
是的。
其次,我们知道
'a'
,但其他单字符串呢?
为了验证前者并测试后者,我运行了以下代码:
import sys
strings = [chr(i) for i in (0, 10, 17, 32, 34, 47, 48, 57, 58, 64, 65, 90, 91, 96, 97, 102, 103, 122, 123, 130, 0x0222, 0x12345)]
sizes = {c: sys.getsizeof(c) for c in strings}
print(sizes)
import numpy as np
sizes = {c: sys.getsizeof(c) for c in strings}
print(sizes)
np.set_printoptions(precision=4, threshold=625, edgeitems=10)
sizes = {c: sys.getsizeof(c) for c in strings}
print(sizes)
import pandas
sizes = {c: sys.getsizeof(c) for c in strings}
print(sizes)
在多个cpython安装中(但在linux或macos上都是64位cpython 3.4或更高版本),我得到了相同的结果:
{'\x00': 50, '\n': 50, '\x11': 50, ' ': 50, '"': 50, '/': 50, '0': 50, '9': 50, ':': 50, '@': 50, 'A': 50, 'Z': 50, '[': 50, '`': 50, 'a': 50, 'f': 50, 'g': 50, 'z': 50, '{': 50, '\x82': 74, 'È¢': 76, 'ð
': 80}
{'\x00': 50, '\n': 50, '\x11': 50, ' ': 50, '"': 50, '/': 50, '0': 50, '9': 50, ':': 50, '@': 50, 'A': 50, 'Z': 50, '[': 50, '`': 50, 'a': 50, 'f': 50, 'g': 50, 'z': 50, '{': 50, '\x82': 74, 'È¢': 76, 'ð
': 80}
{'\x00': 50, '\n': 50, '\x11': 50, ' ': 50, '"': 50, '/': 50, '0': 50, '9': 50, ':': 50, '@': 50, 'A': 50, 'Z': 50, '[': 50, '`': 50, 'a': 50, 'f': 50, 'g': 50, 'z': 50, '{': 50, '\x82': 74, 'È¢': 76, 'ð
': 80}
{'\x00': 50, '\n': 50, '\x11': 50, ' ': 50, '"': 50, '/': 50, '0': 58, '9': 58, ':': 50, '@': 50, 'A': 58, 'Z': 58, '[': 50, '`': 50, 'a': 58, 'f': 58, 'g': 58, 'z': 58, '{': 50, '\x82': 74, 'È¢': 76, 'ð
': 80}
所以,
导入numpy
什么也改变不了
set_printoptions
(大概是米拉杜洛删除评论的原因吧),但是
导入熊猫
做。
它显然影响了ascii数字和字母,但没有其他影响。
另外,如果你改变了所有
print
S至
print(sizes.values())
,因此字符串永远不会为输出进行编码,您会得到相同的结果,这意味着要么不是缓存utf-8,要么就是缓存utf-8,但即使我们不强制它也总是这样。
很明显的可能性是,无论熊猫叫什么,都是用
legacy
PyUnicode
API
为所有ascii数字和字母生成单个字符串。所以这些字符串不是以紧凑的ascii格式结束的,而是以传统的就绪格式结束的,对吧?(有关这意味着什么的详细信息,请参见
the comments in the source
(第三章)
不。使用我的代码
superhackyinternals
,我们可以看到它仍然是紧凑的ascii格式:
import ctypes
import sys
from internals import PyUnicodeObject
s = 'a'
print(sys.getsizeof(s))
ps = PyUnicodeObject.from_address(s)
print(ps, ps.kind, ps.length, ps.interned, ps.ascii, ps.compact, ps.ready)
addr = id(s) + PyUnicodeObject.utf8_length.offset
buf = (ctypes.c_char * 2).from_address(addr)
print(addr, bytes(buf))
import pandas
print(sys.getsizeof(s))
s = 'a'
ps = PyUnicodeObject.from_address(s)
print(ps, ps.kind, ps.length, ps.interned, ps.ascii, ps.compact, ps.ready)
addr = id(s) + PyUnicodeObject.utf8_length.offset
buf = (ctypes.c_char * 2).from_address(addr)
print(addr, bytes(buf))
我们可以看到大熊猫的大小从50变为58,但它们的领域仍然是:
<__main__.PyUnicodeObject object at 0x101bbae18> 1 1 1 1 1 1
换句话说,是
1BYTE_KIND
,长度1,凡人实习,ascii,紧凑,准备就绪。
但是,如果你看
ps.wstr
,在pandas之前是空指针,而在pandas之后是指向
乌恰
一串
w"a\0"
是的。以及
str.__sizeof__
接受这个
wstr
考虑到大小。
所以,问题是,如何得到一个ascii压缩字符串,它有一个
wstr
价值?
简单:你打电话
皮尤尼科德
它(或其他不推荐使用的函数或宏之一,这些函数或宏访问3.2样式的本机
乌恰*
内部存储。本机内部存储实际上不存在于3.3 +中。因此,为了向后兼容,这些调用是通过动态创建存储来处理的,并将其粘贴到
wstr
成员,并调用适当的
PyUnicode_AsUCS[24]
函数解码到该存储。(除非你处理的是一个紧凑的字符串,其类型恰好与
乌恰
宽度,在这种情况下
wstr
毕竟只是指向本机存储的指针。)
你会想到的
街道Sizeof__
理想情况下包括额外的存储空间,以及
from the source
,你可以看到。
让我们验证一下:
import ctypes
import sys
s = 'a'
print(sys.getsizeof(s))
ctypes.pythonapi.PyUnicode_AsUnicode.argtypes = [ctypes.py_object]
ctypes.pythonapi.PyUnicode_AsUnicode.restype = ctypes.c_wchar_p
print(ctypes.pythonapi.PyUnicode_AsUnicode(s))
print(sys.getsizeof(s))
泰达,我们的50比58。
那么,你怎么知道这个叫什么?
实际上有很多电话
皮尤尼科德
和
PyUnicode_AS_UNICODE
宏和其他调用它们的函数,贯穿熊猫和numpy。所以我在lldb中运行了python并在
皮尤尼科德
,如果调用堆栈帧与上次相同,则使用跳过的脚本。
前几个调用涉及日期时间格式。还有一个只有一封信。堆栈帧是:
multiarray.cpython-36m-darwin.so`UNICODE_setitem + 296
-及以上
multiarray
它是纯蟒蛇一直到
导入熊猫
是的。所以,如果你想知道pandas在哪里调用这个函数,你需要在
pdb
,我还没做。但我想我们已经有足够的信息了。