您正在尝试的是Undefined B行为。这是一个更简单的例子:
code00.py:
#!/usr/bin/env python3
import sys
import _ctypes
def func():
custom_object = [1, 2]
return id(custom_object)
def main():
addr = func()
print("Address: {0:d} (0x{1:016X})".format(addr, addr))
o = _ctypes.PyObj_FromPtr(addr)
print(type(o))
print(o)
print(dir(o))
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main()
print("\nDone.")
输出:
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q057805717]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code00.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32
Address: 2221013745352 (0x000002051EBC3EC8)
<class 'list'>
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q057805717]> echo %errorlevel%
-1073741819
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q057805717]> "e:\Work\Dev\VEnvs\py_064_02.07.15_test0\Scripts\python.exe" code00.py
Python 2.7.15 (v2.7.15:ca079a3ea3, Apr 30 2018, 16:30:26) [MSC v.1500 64 bit (AMD64)] 64bit on win32
Address: 57931144 (0x000000000373F588)
<type 'list'>
[[...]]
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Done.
如所见,程序:
-
Python 3:崩溃
-
Python 2:没有崩溃,但“重建”的对象是垃圾
为什么?
-
custom_object 在 func 中创建
- 正在返回它的地址
- 但是当函数返回时,该对象也被计划删除(在下一步之前可能会或可能不会发生,具体取决于gc)
- 使用了对象地址(悬空指针)
解决这个问题的最明显方法是将对象(您要返回其地址)置于函数之外,以便在使用其地址时它仍然有效。这是您的示例(我将其全部保存在一个文件中,但您可以将其拆分为 2 个文件)。
code01.py:
#!/usr/bin/env python3
import sys
import _ctypes
import itertools
c = [1, 21, 31, 4, 51, 61, 71]
d = ["a1", "b1", "c", "d", "e1", "f", "g1"]
custom_object = itertools.chain(c, d)
def func():
global custom_object
return id(custom_object)
def main():
addr = func()
print("Address: {0:d} (0x{1:016X})".format(addr, addr))
o = _ctypes.PyObj_FromPtr(addr)
print(type(o))
print(o)
print(dir(o))
try:
while True:
print(next(o))
except StopIteration:
pass
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main()
print("\nDone.")
输出:
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q057805717]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code01.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32
Address: 1519278021096 (0x00000161BC06D9E8)
<class 'itertools.chain'>
<itertools.chain object at 0x000002B3795337F0>
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', 'from_iterable']
1
21
31
4
51
61
71
a1
b1
c
d
e1
f
g1
Done.
注意事项: