【问题标题】:The first time I use next to get the elements in the iterator, an exception is thrown第一次使用next获取迭代器中的元素,抛出异常
【发布时间】:2019-09-05 12:40:02
【问题描述】:

有两个 .py 文件

test.py

# coding=utf-8
from itertools import chain

def task():
    c = [1, 21, 31, 4, 51, 61, 71]
    d = ['a1', 'b1', 'c', 'd', 'e1', 'f', 'g1']
    e = chain(c, d)
    return id(e)

test1.py

# coding=utf-8
from test import task
import _ctypes

obj_id = task()
tk = _ctypes.PyObj_FromPtr(int(obj_id))

next(tk)

运行 test1 脚本时出现异常,如下所示:

StopIteration

我需要在一个脚本中返回一个对象地址,并在另一个脚本中通过对象地址获取对象。

备注:这个想法可行吗?

非常感谢!

【问题讨论】:

    标签: python python-2.7 ctypes itertools chain


    【解决方案1】:

    您正在尝试的是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:没有崩溃,但“重建”的对象是垃圾

    为什么?

    1. custom_objectfunc 中创建
    2. 正在返回它的地址
    3. 但是当函数返回时,该对象也被计划删除(在下一步之前可能会或可能不会发生,具体取决于gc
    4. 使用了对象地址(悬空指针

    解决这个问题的最明显方法是将对象(您要返回其地址)置于函数之外,以便在使用其地址时它仍然有效。这是您的示例(我将其全部保存在一个文件中,但您可以将其拆分为 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.
    

    注意事项

    • 这解决了您当前的问题。但无论如何,这个问题有点不寻常。你为什么需要它?应该有其他(更清洁的)方法来实现您的目标。这是设计不良或 XY 问题 的信号。检查[SO]: Is it possible to dereference variable id's?
    • 不依赖[Python 3.Docs]: ctypes - A foreign function library for Python 公共API,这意味着_ctypes.PyObj_FromPtr 将来可能无法使用 Python 版本,或其行为可能会更改,恕不另行通知
      • 通常ctypes 函数只适用于ctypes 对象。有点奇怪,这个对象可以处理任何类型的对象(尽管它返回一个 PyObject(它是 CPython 实现中的“一切之父”)指针)

    【讨论】:

    • 非常感谢您的细心回答。我是菜鸟,有一个python爬虫脚本注册为celery任务,由于采集的数据量大,打算用一个迭代器返回结果,但是celery不能序列化迭代器对象,于是想到了返回一个对象地址
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多