Python изнутри. Введение

http://tech.blog.aknin.name/2010/04/02/pythons-innards-introduction/
  • Перевод
  • Tutorial
Boa constrictor1. Введение
2. Объекты. Голова
3. Объекты. Хвост
4. Структуры процесса

Помимо изучения стандартной библиотеки, всегда интересно, а иногда и полезно, знать, как язык устроен изнутри. Андрей Светлов (svetlov), один из разработчиков Python, советует всем интересующимся серию статей об устройстве CPython. Представляю вам перевод первого эпизода.

Мой друг однажды сказал мне: «Знаешь, для некоторых людей язык C — это просто набор макросов, который разворачивается в ассемблерные инструкции». Это было давно (для всезнаек: да, ещё до появления LLVM), но эти слова хорошо мне запомнились. Может быть, когда Керниган и Ритчи смотрят на C-программу, они на самом деле видят ассемблерный код? А Тим Бёрнерс-Ли? Может он сёрфит интернет по-другому, не так, как мы? И что, в конце концов, Киану Ривз видел в том жутком зелёном месиве? Нет, правда, что, чёрт побери, он там видел?! Эм… вернёмся к программам. Что видит Гвидо ван Россум, когда читает программы на Python?

Этот пост — первый в серии статей о внутренностях Питона. Я верю, что объяснение темы другим людям — это лучший способ разобраться в ней. И мне очень хотелось научиться видеть и понимать „жуткое зелёное месиво“, которое стоит за Python-кодом. В основном я буду писать о CPython 3-й версии, о байт-коде (я не фанат этапа компиляции), но, быть может, не обойду вниманием и многое другое, что связано с исполнением Python-кода любого вида (Unladen Swallow, Jython, Cython и т.п.). Для краткости я пишу Python, подразумевая CPython, если только не говорится другое. Также я подразумеваю POSIX-совместимую ОС или, если это важно, Linux, если не утверждается иное. Если вам интересно, как работает Питон, то советую дочитать этот пост до конца. Вам тем более следует это сделать, если вы хотите контрибьютить в CPython. А можете это сделать для того, чтобы найти ошибки, которые я допустил, посмеяться надо мной и оставить ехидные комментарии, если это ваш единственный способ проявлять свои чувства и эмоции.

Практически всё, о чём я буду писать, можно найти в исходных кодах Питона или в некоторых других хороших источниках (документация, особенно эта и эта страницы, отдельные лекции с PyCon, поиск по python-dev и т.д.). Найти можно всё, но я надеюсь, что мои усилия по объединению всех материалов в один, на который можно подписаться через RSS, облегчат ваши приключения. Я предполагаю, что читатель немного знаком с языком C; с теорией операционных систем; немного с ассемблером любой архитектуры; неплохо с Питоном и комфортно чувствует себя в UNIX (например, легко устанавливает что-либо из исходников). Не переживайте, если вам не хватает опыта во всём этом, но и лёгкого плавания я не обещаю. Если у вас нет настроенного окружения для разработки Питона, предлагаю пройти сюда и выполнить необходимые шаги.

Давайте начнём с того, что вам, наверное, уже известно. Для понимания происходящего мне кажется удобной метафора механизмов. В случае с Питоном это несложно, потому что Питон полагается на виртуальную машину, чтобы делать то, что он делает (как и большинство интерпретируемых языков). Здесь важно правильно понимать термин «виртуальная машина»: думать следует скорее в сторону JVM, нежели VirtualBox (технически, они, по сути, одинаковы, но в реальном мире их, как правило, разделяют). Понимать этот термин проще, как мне кажется, буквально — это механизм, составленный из программ. Ваш процессор — это всего лишь сложная электронная машина, которая принимает на вход машинный код и данные, имеет состояние (регистры), и базируясь на вводных данных и текущем состоянии она выводит в память или на шину новую информацию. Понятно, да? А CPython — это механизм, собранный из программных компонентов, который имеет состояние и обрабатывает инструкции (разные реализации могут использовать разные инструкции). Механизм этот работает в процессе, где находится интерпретатор Питона. Мне нравится эта метафора с «механизмами», и я уже описал её в мельчайших подробностях.

Учтя вышесказанное, давайте оценим с высоты птичьего полёта, что происходит, когда мы запускаем такую команду:

$ python -c 'print("Hello, world!")'

Запускается бинарник Питона, инициализируется стандартная библиотека C (это происходит при запуске практически любого процесса), вызывается main-функция (смотрите исходники ./Modules/python.c: main, из которой вызывается ./Modules/main.c: Py_Main). После некоторых подготовительных шагов (разбор аргументов, учёт переменных окружения, оценка ситуации со стандартными потоками и т.д.), вызывается ./Python/pythonrun.c: Py_Initialize. По большому счёту, в этой функции «создаются» и собираются части, необходимые для запуска CPython-машины, и просто «процесс» превращается в «процесс с интерпретатором Питона внутри». Помимо этого, создаются две очень важные структуры: состояния интерпретатора и состояния потока. Также инициализируется встроенный модуль sys и модуль, в котором содержатся все встроенные функции и переменные. В следующих эпизодах эти шаги будут описаны во всех подробностях.

Имея всё это, Питон ползёт одним из нескольких путей в зависимости от того, что ему скормили: выполнится строка (опция -c), выполнится модуль (опция -m), выполнится файл (явно переданный в командной строке или переданный ядром, если Питон используется как интерпретатор скрипта) или запустится REPL (это особый случай исполнения файла, являющегося интерактивным устройством). В нашем случае, будет выполнена строка, т.к. мы передали опцию -c. Чтобы выполнить эту строку, вызывается ./Python/pythonrun.c: PyRun_SimpleStringFlags. Эта функция создаёт пространство имён __main__, в котором будет выполнена наша строчка кода (где будет храниться a, если выполнить $ python -c 'a=1; print(a)'? Правильно, в этом пространстве). После создания пространства, строка выполняется в нём (точнее, интерпретируется). Чтобы это произошло, для начала нужно преобразовать строку во что-нибудь понятное для машины.

Как и говорил, я не буду акцентировать внимание на парсере и компиляторе Питона. Я не знаток этих областей, меня это не сильно интересует, и, насколько я знаю, в компиляторе Питона нет какой-то особой магии, выходящей за пределы университетского курса по компиляторам. Лишь немного пройдёмся по верхам этих тем и, может быть, вернёмся чуть позже, чтобы рассмотреть некоторые особенности поведения CPython (например, оператор global, который влияет на парсер). В общем, стадии парсинга/компиляции в PyRun_SimpleStringFlags проходят следующим образом: лексический анализ и создание дерева разбора, его преобразование в абстрактное синтаксическое дерево (AST), компиляция AST в объект кода с помощью ./Python/ast.c: PyAST_FromNode. Сейчас можете думать об объекте кода, как о бинарной строке, с которыми могут работать механизмы виртуальной машины Питона — теперь мы готовы к интерпретации.

У нас есть практически пустой __main__, у нас есть объект кода, и мы хотим его исполнить. Что дальше? Всё делает строчка из ./Python/pythonrun.c: run_mod:

v = PyEval_EvalCode((PyObject*)co, globals, locals);

Функция принимает объект кода и пространства имён globals и locals (в нашем случае, они являются вновь созданным пространством имён __main__), создаёт объект фрейма и исполняет его. Вернёмся к Py_Initialize, который определяет состояние потока. Каждый питонячий поток представлен отдельной структурой состояния, которая (помимо всего прочего) указывает на стек выполняемых в текущий момент фреймов. После того, как объект фрейма создан и помещён наверх стека состояния потока, он (точнее, байт-код, на который он указывает) выполняется, операция за операцией, средствами довольно длинной функции ./Python/ceval.c: PyEval_EvalFrameEx.

PyEval_EvalFrameEx принимает фрейм, извлекает коды операций (и операнды, если они имеются; мы ещё поговорим об этом) и выполняет кусочки C-кода, соответствующие кодам операций. Давайте дизассемблируем отрывок Python-кода и посмотрим, как выглядят эти «коды операций»:

>>> from dis import dis # о! удобная функция для дизассемблирования!
>>> co = compile("spam = eggs - 1", "<string>", "exec")
>>> dis(co)
 1           0 LOAD_NAME                0 (eggs)
             3 LOAD_CONST               0 (1)
             6 BINARY_SUBTRACT     
             7 STORE_NAME               1 (spam)
            10 LOAD_CONST               1 (None)
            13 RETURN_VALUE
>>>

… даже без особых знаний, байт-код оказывается достаточно читаемым. «Загружаем» что-то с именем eggs (откуда загружаем? откуда мы это загружаем?) и загружаем константное значение (1), затем выполняется «бинарное вычитание» (что здесь подразумевается под словом «бинарный»? что является операндами?), и так далее.

Как вы могли догадаться, переменные «загружаются» из глобального и локального пространств имён, которые мы видели ранее, на стек операндов (не путайте со стеком исполняющихся фреймов), как раз туда, откуда бинарное вычитание их вытащит, вычтет одну из другой и положит результат обратно на стек. «Бинарное вычитание» — это вычитание одного операнда из другого (отсюда и «бинарное», т.е. здесь нет никакой связи с двоичными числами).

Вы можете сами изучить функцию PyEval_EvalFrameEx в файле ./Python/ceval.c. Она достаточно большая, и по очевидным причинам я не буду описывать её здесь целиком, но покажу код, который исполняется при обработке операции BINARY_SUBTRACT:

TARGET(BINARY_SUBTRACT) {
    PyObject *right = POP();
    PyObject *left = TOP();
    PyObject *diff = PyNumber_Subtract(left, right);
    Py_DECREF(right);
    Py_DECREF(left);
    SET_TOP(diff);
    if (diff == NULL)
        goto error;
    DISPATCH();
}

… вытолкнуть первый операнд, взять со стека второй операнд, передать оба операнда C-функции PyNumber_Subtract, сделать непонятный (мы потом разберёмся с этим) Py_DECREF обоим операндам, переписать верхнее значение стека результатом вычитания и сделать какой-то DISPATCH, если diff не равен NULL. Итак. Хотя мы пока и не понимаем некоторых вещей, я думаю, что вычитание двух чисел в Питоне на самом низком уровне, в целом, понятно. Но чтобы дойти до этой точки у нас ушло примерно полторы тысячи слов!

После того, как фрейм выполнен, PyRun_SimpleStringFlags возвращает код завершения, основная функция проводит чистку (особое внимание мы уделим Py_Finalize), деинициализируется libc (atexit и прочее), и процесс завершается.

Надеюсь, этот пост получился достаточно информативным, и мы впоследствие будем пользоваться им как фундаментом при обсуждении разных частей Питона. У нас ещё немало терминов, к которым предстоит вернуться: интерпретатор, состояние потока, пространство имён, модули, встроенные функции и переменные, объекты кода и фреймов и те непонятные слова DECREF и DISPATCH из обработчика BINARY_SUBTRACT. Также у нас есть ключевой «фантомный» термин, вокруг которого мы блуждали в этой статье, но который не называли по имени — объект. Объектная система CPython важна для понимания того, как оно всё работает, и, я надеюсь, мы обстоятельно обсудим её в следующем посте.

Оставайтесь на связи.

При переводе наверняка кто-то пострадал: смыслы, термины, пресмыкающиеся. Давайте вместе делать мир лучше, пишите об ошибках в комментарии, так надёжнее.

Присоединяйтесь к нам, приходите в Буруки! Будем вместе познавать премудрости современных инструментов и создавать крутые продукты.
Метки:
Буруки 26,78
Компания
Поделиться публикацией
Похожие публикации
Комментарии 60
  • +5
    Видео доклада с python митапа в Минске:
    Никита Лесников – «Беглый обзор внутренностей интерпретатора Python»
    Больше ссылок на материалы тут.
    Мне доклад действительно понравился.
    • 0
      Другие доклады — тоже хороши.
    • +3
      Спасибо за перевод, всегда полезно знать внутренние механизмы того, с чем работаешь. Тем не менее, CPython рано или поздно должен либо быть заменён, либо полностью переписан: в текущей реализации существует довольно много проблем, но внутренняя архитектура (в частности, объектная модель) просто не позволяет сделать это приемлимо быстро. Например, ребята из PyPy (интерпретатор Питона, написанный на Питоне и работающий быстрее, чем CPython) уже предложили способ избавиться от надоедливого GIL, но использовать его для CPython слишком дорого, потому что «we would need to change everything everywhere» (с). То же самое касается вывода типов, JIT, оптимизации памяти и т.д.
      • 0
        И до PyPy дойдём. Корни надо знать, в любом случае.
        • 0
          Поддерживаю, было бы интересно узнать сразу про другие интерпритаторы, может даже со сравнением… не только по скорости выполнения.
          Спасибо за перевод.
        • +5
          Проблема не только в том что «ребята не желают».

          Оставим на минутку вопросы совместимости Python C API и всех уже написанных Python C Extensions (а одно только это требует срочного запуска Python 4 и мучительной миграции в то время как на Python 3 ещё далеко не все перешли).

          Software Transactional Memory всё ещё в стадии эксперимента.
          Армин Риго хорошо поработал, но STM всё еще не вошла в релиз PyPy.
          Потому что тормозит и падает иногда.

          Так что, прошу, не надо категорических заявлений.

          Слова про «вывод типов» — не понял о чём это.

          Про оптимизацию памяти — как раз PyPy кушает её как не родной именно из-за GIT.

          К тому же GIT сильно тормозит запуск консольных утилит.
          Он просто не успевает «разогреться» к тому моменту как программа закончилась.
          По этой же причине юниттесты на PyPy могут выполняться дольше чем на CPython.

          В общем, нет серебрянной пули.
          • 0
            Прошу прощения, вместо GIT нужно читать JIT.
            • +3
              Я немного про другое: не про то, какой крутой PyPy, а про то, что не так с архитектурой CPython, про которую и идёт речь в статье. CPython и все его многочисленные расширения на C — это, по сути, одна большая программа с кучей зависимостей. Вносить серьёзные изменения в ядро такой большой программы сложно, о чём как раз и говорится в указанной мной статье: в дефолтной сборке PyPy STM не используется, и, вполне возможно, никогда не будет, но добавить его для эксперимента было достаточно просто. Точно также просто можно изменить движок RPython, при этом не внеся не единой правки в интерпретатор самого Python. Или наоборот, изменить реализацию Python, не затронув движок. Или добавить ещё одну оптимизацию в JIT. Или вообще убрать этот самый JIT. Или скомпилировать для JVM, CLI, LLVM и т.д. Можете представить, что нужно сделать, чтобы реализовать хоть что-то из этого списка в CPython?

              PyPy здесь как самый яркий контр-пример, в принципе, всё то же самое можно сказать про Jython или IronPython.

              К тому же GIT сильно тормозит запуск консольных утилит.

              Это мы щас про какой JIT говорим? В PyPy он трассирующий, так что вначале код, как и в случае CPython, просто интерпретируюется. Трассировка, конечно, тоже даёт свой overhead, но совсем уж незначительный. Сейчас прогнал мини-тест: СPython показал 9ms, PyPy — 23ms — больше, но для пользователя всё равно незаметно.

              Слова про «вывод типов» — не понял о чём это.

              Type inference, то, что делает RTyper для RPython. Хотя сам Python более динамический, и для него полноценный вывод типов сделать невозможно, но для отдельных объектов или сценариев — почему бы и нет (я в своё время делал что-то похожее для JavaScript и Ruby).

              Насчёт серебряной пули — согласен, но улучшать интерпретатор всё-таки проще, когда есть хорошая модульность и чёткие уровни абстракции, чем когда всё, в том числе и third party библиотеки, напрямую завязаны на ядре.
          • 0
            <удалено>
          • +4
            Молодец! Продолжай дальше!

            Документация внутренности покрывает очень слабо.
            На английском можно найти кое-что (очень мало если честно).
            В основном это исходники и внутренние ресурсы (багтрекер и рассылка).

            На русском практичеси вообще ничего нет.

            Я не хочу заниматься переводами сам, но если тебе это нравится — морально поддерживаю со всех сил.
            • +1
              Спасибо!
              если тебе это нравится
              Мне так учиться интереснее пока что. Пользуюсь.
            • +1
              неймспейс по русски — пространство имён.
              Устоявшегося русскоязычного термина нет — так что, как мне кажется, лучше расшифровывать а не использовать транслитерацию.
              Можно выделить курсивом чтобы подчеркнуть особое использование этих слов.
              • +10
                «Пространство имён» — вполне себе устоявшийся термин.
              • +2
                >И что, в конце концов, Киану Ривз видел в том жутком зелёном месиве?

                Вспомнилось… Пришел знакомый ко мне, который от программинга, а тем более от его веб разновидности далекий. А у меня как раз на сайте какая-то проблема была и я решил глянуть в логи апачевские что там. Сижу себе и для него, чтобы скучно не было, комментирую примерно так «вот с яндекса человек зашел», «вот зашел и вышел», «вот, вроде бы с Москвы».

                На что знакомый пристально посмотрел на меня, улыбнулся и говорит:
                — Ты читаешь матрицу.
                • –1
                  Может ролик 30-секундный сделаете как это было?)
                  • 0
                    это не совсем мой талант :)
                • –6
                  Cвоей вредной картинкой вы укрепляете связь «Пайтон → Питон → змея». Лучше бы фотку Джона Клиза поставили.
                  • +15
                    А ничего, что у них на логотипе змея?
                    image
                    • +2
                      Да и просто python с английского — питон )
                      • –1
                        А John Smith с английского — Сортир Кузнец, это ещё не значит, что надо рисовать очко и кузню. Язык назван в честь Монти Пайтона, так случилось, что питон звучит по-английски пайтон, так же как второе слово в «Монти Пайтон». Мне не хочется быть Капитаном, но придётся, видимо.

                        Таким образом змея в логотипе не отрывает Пайтон от его посвящения — для англоязычного человека эта змея — пайтон, звучит как Пайтон в Монти Пайтоне.

                        В русском же языке «Питон» и Пайтон по звучанию различаются, ассоциация разрывается. Кроме того, перевод названия — бред. Никто не говорит «Три Звезды Эс 4», все говорят «Самсунг Эс 4».
                        • +9
                          Сам Гвидо пишет, что не смотря на то, что язык назван в честь Монти Пайтона, логотип и талисман языка — змея. Поэтому думаю ничего страшного, что python — питон, и таких адаптированных переводов есть не мало.
                          • +1
                            Пишешь-пишешь, а никто не читает. В английском нет ничего страшного в такой ассоциации. Там эти два слова одинаково произносятся.
                            • +3
                              Простите, а Adobe вы тоже говорите Эдоуби? Ну так, чтоб не дай бог… Ну и на вскидку нашел сразу 3 поста на Хабре о том, что как в их заграницах произносится, но как не говорят у нас.
                              • +2
                                А вы это слово как-то переводите и в переведённом виде читаете что ли?
                                • 0
                                  «Это» — это какое?
                                  • +1
                                    Adobe, какое же ещё? Вы же о нём писали или о чём-то другом?
                                    • 0
                                      Сложно ответить на вопрос. Ибо английский знаю довольно не плохо, и у меня уже нет особого разделения на «перевести и прочитать как перевод» и «прочитать оригинал». При этом для себя я вполне допускаю искажение оригинального произношения при употреблении иностранных слов.
                                      • +2
                                        Пайтон → Питон, это не искажение, это перевод названия.
                              • 0
                                Можно уточнить, какие именно два слова? Фамилия того самого Монти — «Питонов» (другое их самоназвание — Pythons), это то же самое слово, а не какое-то другое.
                                • 0
                                  Фамилия? Какая фамилия?

                                  «Монти Пайтон» — именно так называлась эта британская группа комиков.
                                  • 0
                                    Если вы прочитаете историю возникновения этой группы, то почерпнете, что «Монти Пайтон» — выдуманное имя и фамилия. Более того, их называли Pythons (ну как в русском языке было бы Ивановы). Если допустить возможность ассоциации данной фамилии со змеей — всё ок.
                                    • 0
                                      Ссылку приведите, не смог найти ничего, подтверждающее вашу версию.

                                      Вы с названием их шоу «Monty Python’s Flying Circus» (Летающий Цирк Монти Пайтона) не путаете, случаем?
                                      • 0
                                        На вскидку вот или вот про историю создания сочетания «Monty Python».

                                        Тут рассказывается, что их называли Pythons, и что до circus их не существовало как «Monty Python» (я про название), хотя и выступления у них до этого были.
                                        • +1
                                          Тем не менее, я нигде не вижу чтобы их называли «Monty Pythons». Везде либо «The Pythons» («те самые Пайтоны», если грубо), либо «группа Monty Python».

                                          Гвидо же назвал язык в честь Летающего Цирка Монти Пайтона — Пайтоном, а не Пайтонсом.
                                        • 0
                                          Так это ж не его версия, это версия создателей труппы: www.fanpop.com/clubs/monty-python-and-the-holy-grail/videos/8197180/title/michael-palin-origin-name-monty-python

                                          Создатели решили, что Монтгомери (или Монти) Питон — по-английски юморное, а значит подходящее, имя для вымышленного персонажа, в честь которого можно назвать их летающий цирк. Вполне себе название в лучших традициях жанра нонсенс. Это как у нас, скажем, «Квадратная бригада Евстигнея Помидорова».

                                          А вообще вы зря так напрягаетесь на тему ассоциации со змеёй, даже Гвидо давным-давно перестал напрягаться %)
                              • –2
                                Раз уж такой офтопик, не могу не спросить, как ты пишешь и произносишь слово Android? «Андро́йд» или «Андрои́д».
                                • +1
                                  Андро́ид.
                                  • 0
                                    Но ведь это слово от английского слова Android, «человекоподобый робот», в котором звук «ɔɪ», такой же как в словах «бой», «чойс». Андрои́д — это совсем другое слово, просто «человекоподобый» из греческого. И как у остальных греческих слов на «-ои́д», ударение должно быть на последний слог. Ассоциация должна сохраняться, нужно писать «Андро́йд».
                                    • +3
                                      По-английски я, вероятно, буду говорить иначе. По-русски же транскрипция (не перевод) слова «андроид» именно такая.
                                      • +1
                                        Да вы, bolk, знатный тролль, Вон какой срач развели не по теме.
                                        • +1
                                          Да уж… (почёсывая затылок)
                                      • +5
                                        Ассоциация должна сохраняться, нужно писать «Андро́йд».
                                        С чего это вдруг? «Андройд» (через и-краткое) вызывает только одну ассоциацию: о неграмотности так пишущего.
                                        • +2
                                          Придумали-то слово в Англии. Но оно явно возникло по аналогии с древнегреческими словами, использует древнегреческие корень и суффикс. Поэтому более логично произносить его согласно традиции произношения других слов подобной структуры.
                                          Вы как произносите название геометрического тела — «эллипсо́йд» или «эллипсои́д»?
                                • +2
                                  Это еще что… на картинке вообще — УДАВ, который съел слона (автор топика ненавидит PHP? :) ). А если верить википедии — удавы и питоны это разные биологические виды.
                                • +4
                                  Вообще-то на картинке удав!
                              • +4
                                Любой разработчик встраиваемых систем (программист микроконтроллеров) видит вместо С программы ассемблерный код :-)
                                • +1
                                  Да, у этих ребят совсем другое мышление)
                                  • +1
                                    … и опасно ошибается, как только переходит на что-то новее gcc 2.95
                                    • 0
                                      Что есть то есть. Потом ещё привычка микрооптимизации сохраняется даже при переходе на языки, где это бесполезно.
                                  • +1
                                    Я знаю от куда картинка у сабжа:
                                    $ aptitude moo -vvvvv
                                  • +1
                                    Каждый питонячий поток представлен отдельной структурой состояния

                                    Ох уж этот питонячий поток :) Скорее всего стоит перевести как: Каждый поток в питоне представлен отдельной структорой состояния.

                                    Но если кто-то возился с потоками в питоне и понимает все недостатки GIL, для тех «оригинальный перевод» может быть наиболее подходящим так как порой выразиться хочется еще более «элегантно».
                                    • +1
                                      Статьи реально отличные, когда писал смешанный отладчик для питона в PTVS — они очень помогли разобраться в исходниках интерпретатора.

                                      Кстати, если кто хочет не просто читать статьи, а на самом деле подебажить ceval.c на живом питоновском коде, у нас есть для этого забавная недокументированная фича — включается вот этим .reg, и в контекстном меню у окон отладчика появляются пункты «Show Native Python Frames» и «Use Python Stepping».

                                      Первая опция включает показ фреймов из python*.dll, параллельно с соответствующими им питоновскими фреймами (они мапятся на нативные фреймы в PyEval_EvalFrameEx) — в результате получается что-то такое:



                                      Хорошо видно, как работает вызов функции, например. Ну и отображение сишных PyObject* в виде питоновских объектов помогает быстрее разобраться, что там происходит.

                                      Вторая опция позволяет выключать специальную обработку пошаговой отладки внутри python*.dll, чтобы команды Step In / Step Out / Step Over работали не на уровне питоновского кода, а на уровне C. Т.е. чтобы можно было пошагово ходить внутри главного цикла интерпретатора.
                                      • НЛО прилетело и опубликовало эту надпись здесь
                                        • 0
                                          С руби я до сих пор не подружился, поэтому могу дать не самые лучшие ссылки.

                                          После недолгих поисков наткнулся на обзорный пост, в котором есть ссылка на книгу Ruby Under a Microscope. Надеюсь, это то, что нужно. Если купите, напишите, пожалуйста, хорошая ли книжка.
                                        • +1
                                          Спасибо за проделанную работу. С большим удовольствием почитаю продолжение, подобный материал на вес золота.

                                          P.S. Buruki — это такой локализованный и адаптированный GetGoing?
                                          • +1
                                            Как и GetGoing мы даём фичи для гибкого и спонтанного поиска. Фича-мечта, к которой мы идём — отвечать на вопрос пользователя „хочу интересно и за ХХХ рублей“ и всегда угадывать лучший ответ на этот вопрос.
                                          • 0
                                            .

                                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                            Самое читаемое