Как работает доход в коде Python C, хорошая и плохая часть

Недавно я изучал код Python. Я знаю, как использовать генераторы (далее, отправить и т. Д.), Но это интересно понять, читая код Python C.

Я нашел код в Object / genobject.c , и это не так сложно (но все же нелегко) понять. Поэтому я хочу знать, как это работает, и убедитесь, что у меня нет недоразумений относительно генераторов в Python.

Я знаю, что все вызывает

static PyObject * gen_send_ex(PyGenObject *gen, PyObject *arg, int exc) 

и результат возвращается из PyEval_EvalFrameEx который выглядит как динамическая структура фрейма, могу ли я понять его как stack или что-то еще?

Ок. Похоже, что Python хранит некоторый контекст в памяти (я прав?). Похоже, каждый раз, когда мы используем yield, он создает генератор и сохраняет контекст в памяти, хотя и не все функции и vars.

Я знаю, если у меня есть большая петля или большие данные для синтаксического анализа, урожай потрясающий, это экономит много памяти и делает ее простой. Но некоторые мои товарищи по работе любят использовать урожай везде, как и возвращение. Это непросто читать и понимать код, а Python сохраняет контекст для большей части функции, которая никогда не может быть вызвана снова. Это плохая практика?

Итак, вопросы:

  1. Как работает PyEval_EvalFrameEx .
  2. Использование памяти урожая.
  3. Это плохая практика использования урожайности во всем мире.

И я обнаружил, что если у меня есть генератор, функция gen_send_ex будет вызываться дважды, почему?

 def test(): while 1: yield 'test here' test().next() 

Он будет вызывать gen_send_ex дважды, первый раз без аргументов, с аргументами второй раз и получить результат.

Спасибо за ваше терпение.

    One Solution collect form web for “Как работает доход в коде Python C, хорошая и плохая часть”

    Я видел эти статьи:

    В этой статье рассказывается, как работает PyEval_EvalFrameEx.

    http://tech.blog.aknin.name/2010/09/02/pythons-innards-hello-ceval-c-2/

    В этой статье рассказывается о структуре фрейма в Python.

    http://tech.blog.aknin.name/2010/07/22/pythons-innards-interpreter-stacks/

    Эти два материала очень важны для нас.

    Поэтому позвольте мне сам ответить на вопрос. Я не знаю, прав ли я.

    Если у меня есть недоразумение или совершенно неверное, пожалуйста, дайте мне знать .

    Если у меня есть код:

     def gen(): count = 0 while count < 10: count += 1 print 'call here' yield count 

    Это очень простой генератор.

     f = gen() 

    И каждый раз, когда мы называем это, Python создает объект-генератор.

     PyObject * PyGen_New(PyFrameObject *f) { PyGenObject *gen = PyObject_GC_New(PyGenObject, &PyGen_Type); if (gen == NULL) { Py_DECREF(f); return NULL; } gen->gi_frame = f; Py_INCREF(f->f_code); gen->gi_code = (PyObject *)(f->f_code); gen->gi_running = 0; gen->gi_weakreflist = NULL; _PyObject_GC_TRACK(gen); return (PyObject *)gen; } 

    Мы могли бы увидеть, что он инициирует объект-генератор. И запустите Frame .

    Все, что нам нравится f.send() или f.next() , будет вызывать gen_send_ex и код ниже:

     static PyObject * gen_iternext(PyGenObject *gen) { return gen_send_ex(gen, NULL, 0); } static PyObject * gen_send(PyGenObject *gen, PyObject *arg) { return gen_send_ex(gen, arg, 0); } 

    Только разница между двумя функциями – arg, send – send arg, next send NULL.

    gen_send_ex код ниже:

     static PyObject * gen_send_ex(PyGenObject *gen, PyObject *arg, int exc) { PyThreadState *tstate = PyThreadState_GET(); PyFrameObject *f = gen->gi_frame; PyObject *result; if (gen->gi_running) { fprintf(stderr, "gi init\n"); PyErr_SetString(PyExc_ValueError, "generator already executing"); return NULL; } if (f==NULL || f->f_stacktop == NULL) { fprintf(stderr, "check stack\n"); /* Only set exception if called from send() */ if (arg && !exc) PyErr_SetNone(PyExc_StopIteration); return NULL; } if (f->f_lasti == -1) { fprintf(stderr, "f->f_lasti\n"); if (arg && arg != Py_None) { fprintf(stderr, "something here\n"); PyErr_SetString(PyExc_TypeError, "can't send non-None value to a " "just-started generator"); return NULL; } } else { /* Push arg onto the frame's value stack */ fprintf(stderr, "frame\n"); if(arg) { /* fprintf arg */ } result = arg ? arg : Py_None; Py_INCREF(result); *(f->f_stacktop++) = result; } fprintf(stderr, "here\n"); /* Generators always return to their most recent caller, not * necessarily their creator. */ Py_XINCREF(tstate->frame); assert(f->f_back == NULL); f->f_back = tstate->frame; gen->gi_running = 1; result = PyEval_EvalFrameEx(f, exc); gen->gi_running = 0; /* Don't keep the reference to f_back any longer than necessary. It * may keep a chain of frames alive or it could create a reference * cycle. */ assert(f->f_back == tstate->frame); Py_CLEAR(f->f_back); /* If the generator just returned (as opposed to yielding), signal * that the generator is exhausted. */ if (result == Py_None && f->f_stacktop == NULL) { fprintf(stderr, "here2\n"); Py_DECREF(result); result = NULL; /* Set exception if not called by gen_iternext() */ if (arg) PyErr_SetNone(PyExc_StopIteration); } if (!result || f->f_stacktop == NULL) { fprintf(stderr, "here3\n"); /* generator can't be rerun, so release the frame */ Py_DECREF(f); gen->gi_frame = NULL; } fprintf(stderr, "return result\n"); return result; } 

    Похоже, что Generator Object является контроллером своего собственного фрейма, который называется gi_frame.

    Я добавляю fprintf (…), поэтому давайте запустим код.

     f.next() f->f_lasti here call here return result 1 

    Итак, сначала он переходит к f_lasti (это целочисленное смещение в байтовый код последних выполненных команд, инициализированный до -1), и да, это -1, но без аргументов, тогда функция продолжается.

    Затем переходим here , самое главное, это PyEval_EvalFrameEx. PyEval_EvalFrameEx реализует цикл вычисления CPython, мы можем, что он запускает каждый код (на самом деле это код операции Python) и запускает print 'call here' строки print 'call here' , он печатает текст.

    Когда код уходит, Python сохраняет контекст с помощью фреймового объекта (мы могли бы искать Call Stack). Отдайте значение и откажитесь от контроля над кодом.

    После того, как все будет сделано, return result и покажите значение 1 в терминале.

    В следующий раз, когда мы запустим next (), он не пойдет в область f_lasti . Это показывает:

     frame here call here return result 2 

    Мы не отправили arg, так что получим результат от PyEval_EvalFrameEx, а результат – 2.

      Interesting Posts

      Скремблирование ajax-страниц с использованием python

      Является ли хорошей практикой зависимость от python с … как выражение

      Как отключить обработку файлов cookie с помощью библиотеки запросов Python?

      При создании пользовательской модели в Django в чем разница между наследованием от моделей. Модел и AuthUser?

      Определение того, является ли текст английским (навалом)

      разбор математического выражения в python и решение найти ответ

      Как получить корневой путь приложения в GAE

      Сделать словарь из списка с помощью python

      Python читает из аргументов командной строки или stdin

      'TypeError: не может умножить последовательность на non-int типа' float '' в условии if. в Python

      Как сохранить настройки приложения в файле конфигурации?

      Обработка нескольких значений для одного параметра с помощью getopt / optparse?

      Как ссылаться на имена столбцов таблицы Excel в XLWings?

      django изменить порт запускает по умолчанию

      Получение даты из сложной строки в Python

      Python - лучший язык программирования в мире.