Очевидно, что вы делаете не то, что, как вы думаете, вы делаете.
self.character[0]
не возвращает адрес / ссылку первого символа (как это было бы в случае массиванапример), но Py_UCS4
-значение (т. е. присвоенное 32-битное целое число), которое копируется в (локальную, временную) переменную в стеке.
В вашей функции, <uintptr_t>&self.character[0]
возвращает вам адрес локальной переменной в стеке, который на каждый шанс всегда один и тот же, потому что при вызове memory
всегда существует один и тот же макет стека.
Чтобы сделать его более понятным, здесьразница с char * c_string
, где &c_string[0]
дает вам адрес первого символа в c_string
.
Сравнить:
%%cython
from libc.stdint cimport uintptr_t
cdef char *c_string = "name";
def get_addresses_from_chars():
for i in range(4):
print(<uintptr_t>&c_string[i])
cdef str py_string="name";
def get_addresses_from_pystr():
for i in range(4):
print(<uintptr_t>&py_string[i])
Сейчас:
>>> get_addresses_from_chars() # works - different addresses every time
# ...7752
# ...7753
# ...7754
# ...7755
>>> get_addresses_from_pystr() # works differently - the same address.
# ...0672
# ...0672
# ...0672
# ...0672
Вы можете видеть это так: c_string[...]
это cdef
функциональность, но py_string[...]
является Python-функциональностью и поэтому не может возвращать адрес для каждой конструкции.
Чтобы повлиять на макет стека, вы можете использовать рекурсивную функцию:
def memory(self, level):
if level==0 :
return <uintptr_t>&self.character[0]
else:
return self.memory(level-1)
Теперьвызов его с помощью a.memory(0)
, a.memory(1)
и т. д. даст вам другие адреса (если только не начнется оптимизация хвостового вызова, я не верю, что это произойдет, но вы можете отключить оптимизацию (-O0
) просточтобы быть уверенным).Поскольку в зависимости от level
/ recursion-глубина локальная переменная, адрес которой будет возвращен, находится в другом месте в стеке.
Чтобы увидеть, интернированы ли объекты Unicode, онадостаточно использовать id
, который возвращает адрес объекта (это детали реализации CPython), поэтому вам вообще не нужен Cython:
>>> id(a.character) == id(a2.character)
# True
или в Cython, делая то же самое, чтоid
делает (немного быстрее):
%%cython
from libc.stdint cimport uintptr_t
from cpython cimport PyObject
...
def memory(self):
# cast from object to PyObject, so the address can be used
return <uintptr_t>(<PyObject*>self.character)
Вам необходимо привести object
к PyObject *
, чтобы Cython позволил получить адрес переменной.
А теперь:
>>> ...
>>> print(a.memory(), a2.memory(), b.memory())
# ...5800 ...5800 ...5000
Если вы хотите получить адрес первой кодовой точки в объекте Unicode (который не совпадает с адресом строки), вы можетеиспользуйте <PY_UNICODE *>self.character
, который Cython заменит при вызове PyUnicode_AsUnicode
, например:
%%cython
...
def memory(self):
return <uintptr_t>(<Py_UNICODE*>self.character), id(self.character)
и теперь
>>> ...
>>> print(a.memory(), a2.memory(), b.memory())
# (...768, ...800) (...768, ...800) (...144, ...000)
т.е. "a"
интернировани имеет адрес, отличный от "b"
, а буфер кодовых точек имеет адрес, отличный от объектов, содержащих его (как и следовало ожидать).