[Python] str + глубина рекурсии

psihodog

Есть три варианта:
class A(object):
    def __init__(self, i):
     self.i = i
    def __str__(self):
     print self.i
     return str(A(self.i + 1

class A(object):
    def __init__(self, i):
     self.i = i
    def __str__(self):
     print self.i
     return A.__str__(A(self.i + 1

class A(object):
    def __init__(self, i):
     self.i = i
    def __str__(self):
     print self.i
     return A(self.i + 1).__str__

Как известно, по-умолчанию maximum recursion depth == 1000.
Если теперь написать str(A(0 то перед тем как свалиться с "maximum recursion depth exceeded",
первый вариант досчитает только до 200, второй до 333, и только последний до честной тысячи. (плюс-минус...)
Кто-нть может пояснить эффект?

danilov

А на стектрейсе не показываются промежуточные вызовы?
Может, у мебя их в первом случае 5 (там точно str проскакивает)
во втором, соотвествстенно, 3 (__str__ -> __str__(self) - возможно тоже как-то средствами языка сделано)
А в третьем явный вызов __str__(self) из него же

pitrik2

первый вариант досчитает только до 200, второй до 333, и только последний до честной тысячи. (плюс-минус...)
а у меня второй до 1000 а последний до 333
чяднт?

psihodog

да, сорри, перепутал.

psihodog

в том трейсе, который печатается traceback'ом и вываливается с эксепшеном ничего такого нет:
  File "a.py", line 9, in __str__
return str(A(self.i + 1
File "a.py", line 9, in __str__
return str(A(self.i + 1
File "a.py", line 9, in __str__
return str(A(self.i + 1
File "a.py", line 9, in __str__
return str(A(self.i + 1

pitrik2

вот держи результат команды pstack для ввсех трёх
fffffd7ffef8c877 string_new + fb
fffffd7ffef9d499 type_call + bd
fffffd7ffeea51ee PyObject_Call + 106
fffffd7fff06d5e9 do_call + 409
fffffd7fff06c10f call_function + f97
fffffd7fff062ec8 PyEval_EvalFrameEx + d0b8
fffffd7fff067488 PyEval_EvalCodeEx + 1d80
fffffd7ffef14f9f function_call + 43f
fffffd7ffeea51ee PyObject_Call + 106
fffffd7ffeed88c6 instancemethod_call + 4aa
fffffd7ffeea51ee PyObject_Call + 106
fffffd7fff06ad35 PyEval_CallObjectWithKeywords + 20d
fffffd7ffefbc7e9 slot_tp_str + 79
fffffd7ffef5869c _PyObject_Str + 1f0
fffffd7ffef588b6 PyObject_Str + 1e
fffffd7ffef8c877 string_new + fb
fffffd7ffef9d499 type_call + bd
fffffd7ffeea51ee PyObject_Call + 106
fffffd7fff06d5e9 do_call + 409
fffffd7fff06c10f call_function + f97
fffffd7fff062ec8 PyEval_EvalFrameEx + d0b8
fffffd7fff067488 PyEval_EvalCodeEx + 1d80
fffffd7fff055d9d PyEval_EvalCode + 75
fffffd7fff0c30bd run_mod + ad
fffffd7fff0c2fc9 PyRun_FileExFlags + 15d
fffffd7fff0c078f PyRun_SimpleFileExFlags + 4eb
fffffd7fff0bf713 PyRun_AnyFileExFlags + ef
fffffd7fff0f258d Py_Main + 1585
00000000004009a0 main + 2c
000000000040080c ?
fffffd7fff06c5dd fast_function + 319
fffffd7fff06c0d0 call_function + f58
fffffd7fff062ec8 PyEval_EvalFrameEx + d0b8
fffffd7fff06c5dd fast_function + 319
fffffd7fff06c0d0 call_function + f58
fffffd7fff062ec8 PyEval_EvalFrameEx + d0b8
fffffd7fff067488 PyEval_EvalCodeEx + 1d80
fffffd7ffef14f9f function_call + 43f
fffffd7ffeea51ee PyObject_Call + 106
fffffd7ffeed88c6 instancemethod_call + 4aa
fffffd7ffeea51ee PyObject_Call + 106
fffffd7fff06ad35 PyEval_CallObjectWithKeywords + 20d
fffffd7ffefbc7e9 slot_tp_str + 79
fffffd7ffef5869c _PyObject_Str + 1f0
fffffd7ffef588b6 PyObject_Str + 1e
fffffd7ffef8c877 string_new + fb
fffffd7ffef9d499 type_call + bd
fffffd7ffeea51ee PyObject_Call + 106
fffffd7fff06d5e9 do_call + 409
fffffd7fff06c10f call_function + f97
fffffd7fff062ec8 PyEval_EvalFrameEx + d0b8
fffffd7fff067488 PyEval_EvalCodeEx + 1d80
fffffd7fff055d9d PyEval_EvalCode + 75
fffffd7fff0c30bd run_mod + ad
fffffd7fff0c2fc9 PyRun_FileExFlags + 15d
fffffd7fff0c078f PyRun_SimpleFileExFlags + 4eb
fffffd7fff0bf713 PyRun_AnyFileExFlags + ef
fffffd7fff0f258d Py_Main + 1585
00000000004009a0 main + 2c
000000000040080c ?
fffffd7fff06c10f call_function + f97
fffffd7fff062ec8 PyEval_EvalFrameEx + d0b8
fffffd7fff067488 PyEval_EvalCodeEx + 1d80
fffffd7ffef14f9f function_call + 43f
fffffd7ffeea51ee PyObject_Call + 106
fffffd7ffeed88c6 instancemethod_call + 4aa
fffffd7ffeea51ee PyObject_Call + 106
fffffd7fff06d5e9 do_call + 409
fffffd7fff06c10f call_function + f97
fffffd7fff062ec8 PyEval_EvalFrameEx + d0b8
fffffd7fff067488 PyEval_EvalCodeEx + 1d80
fffffd7ffef14f9f function_call + 43f
fffffd7ffeea51ee PyObject_Call + 106
fffffd7ffeed88c6 instancemethod_call + 4aa
fffffd7ffeea51ee PyObject_Call + 106
fffffd7fff06d5e9 do_call + 409
fffffd7fff06c10f call_function + f97
fffffd7fff062ec8 PyEval_EvalFrameEx + d0b8
fffffd7fff067488 PyEval_EvalCodeEx + 1d80
fffffd7ffef14f9f function_call + 43f
fffffd7ffeea51ee PyObject_Call + 106
fffffd7ffeed88c6 instancemethod_call + 4aa
fffffd7ffeea51ee PyObject_Call + 106
fffffd7fff06ad35 PyEval_CallObjectWithKeywords + 20d
fffffd7ffefbc7e9 slot_tp_str + 79
fffffd7ffef5869c _PyObject_Str + 1f0
fffffd7ffef588b6 PyObject_Str + 1e
fffffd7ffef8c877 string_new + fb
fffffd7ffef9d499 type_call + bd
fffffd7ffeea51ee PyObject_Call + 106
fffffd7fff06d5e9 do_call + 409
fffffd7fff06c10f call_function + f97
fffffd7fff062ec8 PyEval_EvalFrameEx + d0b8
fffffd7fff067488 PyEval_EvalCodeEx + 1d80
fffffd7fff055d9d PyEval_EvalCode + 75
fffffd7fff0c30bd run_mod + ad
fffffd7fff0c2fc9 PyRun_FileExFlags + 15d
fffffd7fff0c078f PyRun_SimpleFileExFlags + 4eb
fffffd7fff0bf713 PyRun_AnyFileExFlags + ef
fffffd7fff0f258d Py_Main + 1585
00000000004009a0 main + 2c
000000000040080c ?

tipnote

Хз, проверил первый вариант - примерно 500, что логично, так как по паре str и __str__ на одну полезную итерацию.
$ python
Python 2.5.5 (r255:77872, Apr 21 2010, 08:40:04)
[GCC 4.4.3] on linux2

tipnote

Проверил остальные два случая - по тысяче.
Укажи свой интерпретатор, сборку, etc.

psihodog

Python 2.6.4 (r264:75706, Jan 17 2010, 02:58:40)
[GCC 4.2.1 20070719 [FreeBSD]] on freebsd7

Python 2.7 (r27:82525, Jul  4 2010, 09:01:59) [MSC v.1500 32 bit (Intel)] on win32

Python 3.1.1 (r311:74483, Aug 17 2009, 17:02:12) [MSC v.1500 32 bit (Intel)] on win32

везде поведение одинаковое.

psihodog

долго во всё это втыкал, но так ничего и не понял :)

pitrik2

долго во всё это втыкал, но так ничего и не понял :)
начало стека как видим у всех одинаковое
а дальше циклически повторяются куски
fffffd7ffefbc7e9 slot_tp_str + 79
fffffd7ffef5869c _PyObject_Str + 1f0
fffffd7ffef588b6 PyObject_Str + 1e
fffffd7ffef8c877 string_new + fb
fffffd7ffef9d499 type_call + bd
fffffd7ffeea51ee PyObject_Call + 106
fffffd7fff06d5e9 do_call + 409
fffffd7fff06c10f call_function + f97
fffffd7fff062ec8 PyEval_EvalFrameEx + d0b8
fffffd7fff067488 PyEval_EvalCodeEx + 1d80
fffffd7ffef14f9f function_call + 43f
fffffd7ffeea51ee PyObject_Call + 106
fffffd7ffeed88c6 instancemethod_call + 4aa
fffffd7ffeea51ee PyObject_Call + 106
fffffd7fff06ad35 PyEval_CallObjectWithKeywords + 20d
тут дофига всего участвует в call stack
и слоты, и string_new, и конструктор instancemethod_call
5 это видимо: slot_tp_str, instancemethod_call, function_call, call_function, string_new
fffffd7fff06c5dd fast_function + 319
fffffd7fff06c0d0 call_function + f58
fffffd7fff062ec8 PyEval_EvalFrameEx + d0b8
тут вощем всё понятно
сначала создаётся A
а потом у него зовётся __str__
т.е. "создание" A в call stack не попадает
fffffd7ffeea51ee PyObject_Call + 106
fffffd7fff06d5e9 do_call + 409
fffffd7fff06c10f call_function + f97
fffffd7fff062ec8 PyEval_EvalFrameEx + d0b8
fffffd7fff067488 PyEval_EvalCodeEx + 1d80
fffffd7ffef14f9f function_call + 43f
fffffd7ffeea51ee PyObject_Call + 106
fffffd7ffeed88c6 instancemethod_call + 4aa
fffffd7ffeea51ee PyObject_Call + 106
тут видимо 3 вложенных вызова: instancemethod_call, function_callб call_function

tipnote

Посмотрел на 2.6 - повторил твой результат. Мда, буду сидеть на 2.5, пока тройку не вылижут.
Оставить комментарий
Имя или ник:
Комментарий: