Python / C: проанализировать все значения сразу для возврата в Python? - PullRequest
4 голосов
/ 13 декабря 2010

Если вы выводите много значений из C в dict в Python, есть ли лучший (более быстрый и менее подверженный ошибкам) ​​способ сделать это, чем:

    return Py_BuildValue("{s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:(i,i,i,i),s:(i,i,i,i),s:(i,i,i,i)}",
            "jd\0",             spa.jd, //Julian day
            "jc\0",             spa.jc, //Julian century
            "jde\0",            spa.jde, //Julian ephemeris day
            "jce\0",            spa.jce, //Julian ephemeris century
            "jme\0",            spa.jme, //Julian ephemeris millennium
            "l\0",              spa.l, //earth heliocentric longitude [degrees]
            "b\0",              spa.b, //earth heliocentric latitude [degrees]
            "r\0",              spa.r,     //earth radius vector [Astronomical Units, AU]
            "theta\0",          spa.theta, //geocentric longitude [degrees]
            "beta\0",           spa.beta, //geocentric latitude [degrees]
            "x0\0",             spa.x0, //mean elongation (moon-sun) [degrees]
            "x1\0",             spa.x1, //mean anomaly (sun) [degrees]
            "x2\0",             spa.x2, //mean anomaly (moon) [degrees]
            "x3\0",             spa.x3, //argument latitude (moon) [degrees]
            "x4\0",             spa.x4, //ascending longitude (moon) [degrees]
            "del_psi\0",        spa.del_psi, //nutation longitude [degrees]
            "del_epsilon\0",    spa.del_epsilon, //nutation obliquity [degrees]
            "epsilon0\0",       spa.epsilon0, //ecliptic mean obliquity [arc seconds]
            "epsilon\0",        spa.epsilon, //ecliptic true obliquity  [degrees]
            "del_tau\0",        spa.del_tau, //aberration correction [degrees]
            "lamda\0",          spa.lamda, //apparent sun longitude [degrees]
            "nu0\0",            spa.nu0, //Greenwich mean sidereal time [degrees]
            "nu\0",             spa.nu, //Greenwich sidereal time [degrees]
            "alpha\0",          spa.alpha, //geocentric sun right ascension [degrees]
            "delta\0",          spa.delta, //geocentric sun declination [degrees]
            "h\0",              spa.h, //observer hour angle [degrees]
            "xi\0",             spa.xi, //sun equatorial horizontal parallax [degrees]
            "del_alpha\0",      spa.del_alpha, //sun right ascension parallax [degrees]
            "delta_prime\0",    spa.delta_prime, //topocentric sun declination [degrees]
            "alpha_prime\0",    spa.alpha_prime, //topocentric sun right ascension [degrees]
            "h_prime\0",        spa.h_prime, //topocentric local hour angle [degrees],
            "h0_prime\0",       spa.h0_prime,
            "delta_zero\0",     spa.delta_zero,
            "e0\0",             spa.e0, //topocentric elevation angle (uncorrected) [degrees]
            "del_e\0",          spa.del_e, //atmospheric refraction correction [degrees]
            "e\0",              spa.e, //topocentric elevation angle (corrected) [degrees]
            "eot\0",            spa.eot, //equation of time [minutes]
            "srha\0",           spa.srha, //sunrise hour angle [degrees]
            "ssha\0",           spa.ssha, //sunset hour angle [degrees]
            "sta\0",            spa.sta, //sun transit altitude [degrees]
            "zenith\0",         spa.zenith, //topocentric zenith angle [degrees]
            "azimuth180\0",     spa.azimuth180, //topocentric azimuth angle (westward from south) [-180 to 180 degrees]
            "azimuth\0",        spa.azimuth, //topocentric azimuth angle (eastward from north) [   0 to 360 degrees]
            "incidence\0",      spa.incidence, //surface incidence angle [degrees]
            "_suntransit\0",    spa.suntransit, //local sun transit time (or solar noon) [fractional hour]
            "_sunrise\0",       spa.sunrise, //local sunrise time (+/- 30 seconds) [fractional hour]
            "_sunset\0",        spa.sunset, //local sunset time (+/- 30 seconds) [fractional hour]
            "sunrise\0",        sunrise_hour, sunrise_min, sunrise_sec, sunrise_microsec,
            "sunset\0",         sunset_hour, sunset_min, sunset_sec, sunset_microsec,
            "noon\0",           transit_hour, transit_min, transit_sec, transit_microsec
            );

Ответы [ 4 ]

2 голосов
/ 13 декабря 2010

Я согласен с @Martin v. Löwis относительно использования препроцессора C и его макроэкономических возможностей, чтобы облегчить хотя бы часть бремени настройки и поддержки чего-то вроде того, что вы делаете. Если вы правильно определите эти макросы, вы можете организовать размещение всей определяющей информации в одном месте в одном заголовочном файле и избежать повторения.

В основном вам нужны две части информации о каждом элементе или паре ключ / значение, которые должны быть включены в создаваемый вами словарь. Одна часть - это то, что помещено в строковый аргумент формата Py_BuildValue(), а вторая - источник ключа и связанного с ним значения.

Вы можете извлечь каждый из этих двух наборов информации, определив, а затем переопределив макросы, используемые по мере необходимости для задачи. Для вашего примера может быть создан следующий заголовочный файл. Обратите внимание, как определяется один из двух различных наборов макросов в зависимости от того, был ли определен FORMAT или FIELDS в то время, когда он был #include d.

// builddict.h -- for defining Py_BuildValue() arguments

// define apppropriate macros for current usage
#ifdef FORMAT
    #define SPA_FIELD_LAST(FIELD)           "s:d"
    #define SPA_FIELD(FIELD)                SPA_FIELD_LAST(FIELD)", "
    #define TIME_FIELD_LAST(NAME)           "s:(i,i,i,i)"
    #define TIME_FIELD(NAME)                TIME_FIELD_LAST(NAME)", "
    #define TIME_KEY_FIELD_LAST(KEY,NAME)   "s:(i,i,i,i)"
    #define TIME_KEY_FIELD(KEY,NAME)        TIME_KEY_FIELD_LAST(KEY,NAME)", "
    #undef FORMAT
#elif defined FIELDS
    #define SPA_FIELD_LAST(FIELD)           #FIELD, spa.FIELD
    #define SPA_FIELD(FIELD)                SPA_FIELD_LAST(FIELD),
    #define TIME_FIELD_LAST(NAME)           #NAME, NAME##_hour, NAME##_min, NAME##_sec, NAME##_microsec
    #define TIME_FIELD(NAME)                TIME_FIELD_LAST(NAME),
    #define TIME_KEY_FIELD_LAST(KEY,NAME)   #KEY, NAME##_hour, NAME##_min, NAME##_sec, NAME##_microsec
    #define TIME_KEY_FIELD(KEY,NAME)        TIME_KEY_FIELD_LAST(KEY,NAME),
    #undef FIELDS
#else
    #error neither FORMAT nor FIELDS usage macros are defined
#endif

SPA_FIELD(jd)               // Julian day
SPA_FIELD(jc)               // Julian century
SPA_FIELD(jde)              // Julian ephemeris day
SPA_FIELD(jce)              // Julian ephemeris century
SPA_FIELD(jme)              // Julian ephemeris millennium
SPA_FIELD(l)                // earth heliocentric longitude [degrees]
SPA_FIELD(b)                // earth heliocentric latitude [degrees]
SPA_FIELD(r)                // earth radius vector [Astronomical Units)  AU]
SPA_FIELD(theta)            // geocentric longitude [degrees]
SPA_FIELD(beta)             // geocentric latitude [degrees]
SPA_FIELD(x0)               // mean elongation (moon-sun) [degrees]
SPA_FIELD(x1)               // mean anomaly (sun) [degrees]
SPA_FIELD(x2)               // mean anomaly (moon) [degrees]
SPA_FIELD(x3)               // argument latitude (moon) [degrees]
SPA_FIELD(x4)               // ascending longitude (moon) [degrees]
SPA_FIELD(del_psi)          // nutation longitude [degrees]
SPA_FIELD(del_epsilon)      // nutation obliquity [degrees]
SPA_FIELD(epsilon0)         // ecliptic mean obliquity [arc seconds]
SPA_FIELD(epsilon)          // ecliptic true obliquity  [degrees]
SPA_FIELD(del_tau)          // aberration correction [degrees]
SPA_FIELD(lamda)            // apparent sun longitude [degrees]
SPA_FIELD(nu0)              // Greenwich mean sidereal time [degrees]
SPA_FIELD(nu)               // Greenwich sidereal time [degrees]
SPA_FIELD(alpha)            // geocentric sun right ascension [degrees]
SPA_FIELD(delta)            // geocentric sun declination [degrees]
SPA_FIELD(h)                // observer hour angle [degrees]
SPA_FIELD(xi)               // sun equatorial horizontal parallax [degrees]
SPA_FIELD(del_alpha)        // sun right ascension parallax [degrees]
SPA_FIELD(delta_prime)      // topocentric sun declination [degrees]
SPA_FIELD(alpha_prime)      // topocentric sun right ascension [degrees]
SPA_FIELD(h_prime)          // topocentric local hour angle [degrees])
SPA_FIELD(h0_prime)
SPA_FIELD(delta_zero)
SPA_FIELD(e0)               // topocentric elevation angle (uncorrected) [degrees]
SPA_FIELD(del_e)            // atmospheric refraction correction [degrees]
SPA_FIELD(e)                // topocentric elevation angle (corrected) [degrees]
SPA_FIELD(eot)              // equation of time [minutes]
SPA_FIELD(srha)             // sunrise hour angle [degrees]
SPA_FIELD(ssha)             // sunset hour angle [degrees]
SPA_FIELD(sta)              // sun transit altitude [degrees]
SPA_FIELD(zenith)           // topocentric zenith angle [degrees]
SPA_FIELD(azimuth180)       // topocentric azimuth angle (westward from south) [-180 to 180 degrees]
SPA_FIELD(azimuth)          // topocentric azimuth angle (eastward from north) [   0 to 360 degrees]
SPA_FIELD(incidence)        // surface incidence angle [degrees]
SPA_FIELD(suntransit)       // local sun transit time (or solar noon) [fractional hour]
SPA_FIELD(sunrise)          // local sunrise time (+/- 30 seconds) [fractional hour]
SPA_FIELD(sunset)           // local sunset time (+/- 30 seconds) [fractional hour]
TIME_FIELD(sunrise)
TIME_FIELD(sunset)
TIME_KEY_FIELD_LAST(noon, transit)  // must use a xxx_LAST macro on last one

// clean up to prevent warnings about redefining macros
#undef SPA_FIELD_LAST
#undef SPA_FIELD
#undef TIME_FIELD_LAST
#undef TIME_FIELD
#undef TIME_KEY_FIELD_LAST
#undef TIME_KEY_FIELD

Как только вы все настроите, ваша функция build_dict() станет чем-то довольно коротким и независимым от того, каким будет фактическое содержание словаря:

// build format string using header
char format_string[] = "{"
    #define FORMAT
    #include "builddict.h"
"}";

// use header again to build list of fields
PyObject* build_dict(SPA spa)
{
    return Py_BuildValue(format_string,
        #define FIELDS
        #include "builddict.h"
    );
}

Хотя это не полностью автоматизирует процесс, но может сильно помочь. Вероятно, существуют дополнительные инструменты обработки текста или C-интерфейса (или вы можете написать свой собственный), которые помогут вам в создании этого единственного заголовочного файла, поскольку он имеет очень унифицированный формат.

2 голосов
/ 13 декабря 2010

Вы можете использовать макросы:

#define ADD_FIELD(F) PyDict_SetItemString(d, #F, spa.F)
ADD_FIELD(jd);
ADD_FIELD(jc);
...

Это предотвратит ошибки в именах строк и форматной строке.Ошибки в перечислении не всех полей нелегко предотвратить AFAICT.

Также вы можете отбросить трейлинг \0;это не служит цели.

2 голосов
/ 13 декабря 2010

Учитывая, что большинство значений, похоже, взято из spa, вероятно, лучше будет инкапсулировать его вместе с другими свободными свойствами в объекте и просто вернуть его.Также datetime.time.

1 голос
/ 13 декабря 2010

Если это необходимо для нескольких структур, то, вероятно, я бы написал небольшой скрипт на python для генерации этого кода, прочитав определение структуры из .h (например, пометив специальным комментарием, какие структуры и поля вам нужныэкспортировать как dict) ... однако последние три поля в показанном случае необходимо было бы добавить вручную к dict.

Я бы не стал делать это только для одной структуры, особенно если структура стабильна.

Рассматривали ли вы экспорт объектов вместо диктов, например, с использованием SIP?

...