Почему при использовании косвенно определенных преобразователей в numpy.genfromtxt () происходит ошибка «RecursionError: превышена максимальная глубина рекурсии»? - PullRequest
1 голос
/ 03 июня 2019

Я предполагаю, что это вопрос только для людей, которым нравится копаться в исходном коде трассировки Python ... но, может быть, кто-то знает ответ изо всех сил.

Это должно быть легко воспроизводимо, см. Код ниже (я полагаю, что в зависимости от вашего оборудования и значения для sys.setrecursionlimit () вам может потребоваться увеличить максимальное количество итераций по сравнению с моим значением 2000).

Это numpy.genfromtxt, читающий CSV-файл с 1 столбцом и 1 строкой, состоящей из одного символа 0. Когда "преобразователи" явно установлены (закомментировано ниже), все хорошо и быстро. Когда «преобразователи» установлены косвенно, как показано в коде, Python делает что-то рекурсивное, что совершенно не нужно, и код завершается с ошибкой где-то между 1400 и 1500 итерациями (на моем компьютере) с ошибкой «RecursionError: превышена максимальная глубина рекурсии». До сбоя кода он становится все медленнее и медленнее по мере увеличения итераций (и, вероятно, глубины рекурсии). Traceback указывает на исходный код, но я не знаю, как в него разобраться.

Вопрос в том, почему этот код не работает точно так же, как код, в котором "конвертеры" установлены явно? Это ошибка или имеет смысл; мой код плох?

#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}, \
                             )

1 Ответ

1 голос
/ 03 июня 2019
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>))}

Обратите внимание, что значение поименованного ключа не изменяется;скорее он добавляет ключ номера столбца с измененным конвертером.

Если вместо этого мы создаем словарь in-line и просто предоставляем лямбду (или функцию), функция не изменяется:

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)>}

Без кодирования (или байтов по умолчанию) добавляется оболочка tobytes, и моей функции передается строка байтов:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...