为什么在numpy.genfromtxt()中使用间接定义的转换器会失败,并出现错误“RecursionError:maximum recursion depth exceeded”?

2024-09-30 14:27:03 发布

您现在位置:Python中文网/ 问答频道 /正文

我猜这是一个问题,只有那些喜欢挑战的人谁挖到源代码的Python溯源。。。但也许有人知道答案从他们的头顶

这应该很容易复制,请参阅下面的代码(我认为,根据您的硬件和sys.setrecursionlimit()的值,您可能需要从我的值2000增加最大迭代次数)

它是numpy.genfromtxt读取一个CSV文件,该文件包含一列和一行,由单个字符0组成。当显式设置“converters”(在下面注释掉)时,一切都很好而且很快。当如代码所示间接设置“converters”时,Python执行的是完全不必要的递归操作,代码在1400到1500次迭代之间失败(在我的计算机上),错误为“RecursionError:maximum recursion depth exceeded”。在代码失败之前,随着迭代(可能还有递归深度)的增加,它变得越来越慢。回溯指向相关的源代码,但我不知道如何深入研究

问题是:为什么这个代码不能像显式设置“转换器”的代码那样工作?是虫子,还是有道理;i、 我的代码不好

#Spyder 3.3.3 | Python 3.7.3 64-bit | Qt 5.9.6 | PyQt5 5.9.2 | Windows 10 

import numpy as np

the_converters = {'data': lambda s : 0} 

jcount = 0
while jcount < 2000:

    jcount = jcount + 1
    print(jcount)

    the_array = np.genfromtxt('recursion_debug.csv', delimiter =',', \
                             names = 'data', \
                             converters = the_converters, \
                             #converters = {'data': lambda s : 0}, \
                             )

Tags: 文件thelambda答案代码numpydata源代码
1条回答
网友
1楼 · 发布于 2024-09-30 14:27:03
In [1]: txt="""0,0 
   ...: 0,0"""         
In [14]: cvt = {'data':lambda s: 10}                                                                                     
In [15]: cvt                                                                                                             
Out[15]: {'data': <function __main__.<lambda>(s)>}
In [16]: np.genfromtxt(txt.splitlines(),delimiter=',',usecols=[0],names='data',converters=cvt)                           
Out[16]: array([10, 10])
In [17]: cvt                                                                                                             
Out[17]: 
{'data': <function __main__.<lambda>(s)>,
 0: functools.partial(<function genfromtxt.<locals>.tobytes_first at 0x7f5e71154bf8>, conv=<function <lambda> at 0x7f5e70928b70>)}

genfromtxt正在修改cvt对象(就地),此效果是累积的:

In [18]: np.genfromtxt(txt.splitlines(),delimiter=',',usecols=[0],names='data',converters=cvt)                           
Out[18]: array([10, 10])
In [19]: cvt                                                                                                             
Out[19]: 
{'data': <function __main__.<lambda>(s)>,
 0: functools.partial(<function genfromtxt.<locals>.tobytes_first at 0x7f5e82ea4bf8>, conv=functools.partial(<function genfromtxt.<locals>.tobytes_first at 0x7f5e71154bf8>, conv=<function <lambda> at 0x7f5e70928b70>))}

请注意,指定的键值不会更改;相反,它使用修改后的转换器添加了一个列编号键

相反,如果我们在线创建字典,并且只提供lambda(或函数),则不会修改函数:

In [26]: cvt = lambda s: 10                                                                                              
In [27]: np.genfromtxt(txt.splitlines(),delimiter=',',usecols=[0],names='data',converters={'data':cvt})                  
Out[27]: array([10, 10])
In [28]: cvt                                                                                                             
Out[28]: <function __main__.<lambda>(s)>

现在创建一个函数,同时显示输入字符串:

In [53]: def foo(s): 
    ...:     print(s) 
    ...:     return '10' 
    ...:                                                                                                                 
In [54]: cvt = {'data':foo}                                                                                              

如果我指定encoding,字典仍会被修改(新键),但函数不会被修改:

In [55]: np.genfromtxt(txt.splitlines(),delimiter=',',usecols=[0],names='data',converters=cvt, encoding=None)            
0
0
0
Out[55]: array(['10', '10'], dtype='<U2')
In [56]: cvt                                                                                                             
Out[56]: {'data': <function __main__.foo(s)>, 0: <function __main__.foo(s)>}

在没有编码(或默认的“bytes”)的情况下,添加tobytes包装器,并将bytestring传递给我的函数:

In [57]: np.genfromtxt(txt.splitlines(),delimiter=',',usecols=[0],names='data',converters=cvt)                           
b'0'
b'0'
b'0'
b'0'
Out[57]: array(['10', '10'], dtype='<U2')
In [58]: cvt                                                                                                             
Out[58]: 
{'data': <function __main__.foo(s)>,
 0: functools.partial(<function genfromtxt.<locals>.tobytes_first at 0x7f5e82e9c730>, conv=<function foo at 0x7f5e7113e268>)}

===

添加functools.partial的代码是旧的Py2到Py3字节到unicode开关的一部分:

   elif byte_converters:
        # converters may use decode to workaround numpy's old behaviour,
        # so encode the string again before passing to the user converter
        def tobytes_first(x, conv):
            if type(x) is bytes:
                return conv(x)
            return conv(x.encode("latin1"))
        import functools
        user_conv = functools.partial(tobytes_first, conv=conv)
    else:
        user_conv = conv

相关问题 更多 >