LINUX.ORG.RU

Очередная безумная идея

 


0

1

Как правило это выглядит так. После написания уравнения, численной схеме и пр., ваяется прототип кода. Ядро пишется на плюсах в виде отдельного модуля, в котором есть класс модели со всякими численными/физическими параметрами и методами (типа задать начальные условия, посчитать че нить, скинуть результат в файл), модуль цепляется к питону через SWIG и дальше из питоньего скрипта им рулится - создается экземпляр класса модели, устанавливаются параметры, запускаются методы.

Дальше это все отлаживается до какого то вида, и вот наступает тот самый момент когда оно вроде работает и надо его активно тестить гоняя серии расчетов.

Что бы не складывать результаты в файлы с именами типа a=1-b=2.34-c=-13.5.dat (а файлов с одного расчета м.б. несколько) давно отработана некая идеология - есть библиотека на питоне, которой для каждого расчета делается уникальная директория куда сваливаются все результаты расчета, туда же автоматом тарятся исходники и пиклятся параметры расчета.

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

calc = CALC(a=1, b=2.5, c=True, N=100)

Эти параметры можно менять при запуске из командной строки. Дальше эти параметры либо пачкой накатываются на модель (весь словарь), либо явно юзаются в скрипте

m = CPPModel()
...
m.init(calc.N)
...
calc в итоге пиклится в директорию расчета.

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

Мой воспаленный мосг родил такую идею. Вот у нас есть скрипт

from model import *
m = CPPModel()
m.a = 1
m.b = 1.5
m.dt = 0.1
m.init(100)
while m.t<10.:
   m.calc(25) # че то считаем
m.dump('result.dat')
Мы делаем так:
from model import *
from racs import calc # тут создается уникальная директория  
# и всяко разно происходит, включая разбор аргументов 
# командной строки
m = calc.control(CPPModel())
m.a = 1
m.b = 1.5
m.dt = 0.1
m.init(100)
while m.t<10.:
   m.calc(25) # че то считаем
m.dump('result.dat')

calc.control возвращает CPPModel() завернутую в оболочку, перехватывающую get/setattr. Все атрибуты которые для оболчки задаются считаются параметрами (их деволтными значениями). Если я в аргументах командной строки напишу a=2 то будет задано m.a=2 Это клево, но вызвыает некоторое... недоумение.

Дальше хуже. При вызове методов, если я хочу управлять параметрами вызова мне придется писать че то типа

calc.N = 100 # дефолтное значение
m.init(calc.N)
или заводить параметр N в CPPModel. Но через оболочку (которая знает ес-но сигнатуры всех функций) я могу всего этого не делать, а отслеживать какие параметры передаются методам (это дефолтные значения) и менять их через аргументы командной строки (типа init=100) или учитывать эти значения при перезапуске. Это еще более клево, но вызывает совсем большое недоумение (поведение нестандартное мягко гря).

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

В общем хотелось бы обсудить;-)

★★★★★

Последнее исправление: cetjs2 (всего исправлений: 1)

спасибо за swig, у меня упрощенная версия описанного была на subprocess и json, сильно тормозила

anonymous
()

В общем хотелось бы обсудить;-)

Давай обсудим. А в чём проблема?

yvv ★★☆
()

Графоманство какое-то. В чем суть проблемы? В большом количестве ключей задаваемых? Заведи параметр «help», который будет описывать остальные скопом или выборочно. Или вообще при запуске указываешь в дополнение к остальным параметрам ключ «help», и оно тебе выплевывает в стдаут описание того, что и с какими параметрами будет высчитываться, вместо того, чтобы считать.

Virtuos86 ★★★★★
()
Ответ на: комментарий от Virtuos86

help это вообще не проблема. Проблема в неоднозначностях, скажем

...
m.a = 1 
...
m.a = 2
...

при запуске

./run.py a=15
и в строчке 'm.a = 2' непонятно чему именно должно быть равно а?

Можно сказать, что в таком подходе код на самом деле некоторый дефолтный шаблон, любой из параметров которого (не знаю как это правильно назвать - речь идет о атрибутах, аргументах методов и кстати о вызовах самих методов тоже, т.е. вместо m.init можно вызвать напр. m.init2 ежели такой есть) может быть изменен при вызове.

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

Пущай умные люди подскажут, cast tailgunner, true_admin, monk;-)

AIv ★★★★★
() автор топика
Ответ на: комментарий от AIv

Проблема в неоднозначностях

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

То есть в calc при выполнении m.a = 1 у тебя будет что-то вроде «если cmd['a'] не заполнено, тогда a = 1».

Соответственно, если у тебя ./run.py a=15, то всюду m.a == 15.

изменять можно только параметры остающиеся постоянными на протяжении трассы выполнения

В смысле, кидать при второй попытке изменить значение? Можно, но зачем? Может же быть что-то вроде

m.a = 1
...
m.b = ...
...
if m.b > 3:
  m.a = 2
Зачем такое запрещать?

monk ★★★★★
()
Ответ на: комментарий от monk

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

Да.

Зачем такое запрещать?

Затем что при m.a==15 условие может иметь вид

if m.b>3:
   m.a = 30 # или 16 или вообще 100500
или этот фрагмент кода может вообще не иметь смысла. Если параметр (как это кстати все таки правильно называется, с учетом методов и аргументов методов?) постоянный, то проблем нету - эта штука единожды устанавливается и дальше как то влияет на работу кода.

Если хочется сделать переменный a, то это надо делать явно, т.е. указывать что a не находтится под контролем и дальше писать

m.a = calc.a
...
if m.b>3:
   m.a = 2*calc.a

AIv ★★★★★
() автор топика
Ответ на: комментарий от AIv

У тебя параметр командной строки всегда перекрывает внутренний.

И, если действительно надо заменить, то надо синтаксически это явно показывать (должно быть не m.a = ...).

Например:

if m.b>3:
   m.set('a', 2*m.a)

или m.real_a = 2*m.a

или (для большей понятности случайному читателю): m.a = ... всегда меняет текущее значение, m.default('a', ...) или m.default_a = ... меняет значение m.a если нет параметра командной строки.

По крайней мере тогда когнитивного диссонанса при чтении не возникнет.

monk ★★★★★
()
Ответ на: комментарий от monk

Один из главных посылов - внесение минимальных изменений в код при подключении этой вундервафли. Я полностью согласен, что синтаксическое выделение поднимет читаемость (сейчас с-но так все и работает), но у меня условно 20 параметров которые задаются единожды, и 2 параметра которые меняются по ходу пьесы. Соответственно:

1) мне проще как то специально обработать 2 меняющихся параметра чем 20 постоянных.

2) надо не забыть это сделать (иначе последствия непредсказуемы), а если я забыл то надо мне как то об этом напомнить.

AIv ★★★★★
() автор топика

Простите, ТС.

Что именно Вам не нравится в существующем подходе https://docs.python.org/2/extending/extending.html ?

Если я в аргументах командной строки напишу a=2 то будет задано m.a=2 Это клево, но вызвыает некоторое... недоумение.

Почему? Вы вызываете свой модуль, задавая аргумент. Что здесь странного?

В общем, если честно, то простите, но ЯННП в чём смысл идеи. Объясните на интуитивно-понятном уровне (на пальцах)?

Moisha_Liberman ★★
()
Ответ на: комментарий от AIv

А. Уже понятнее.

2) надо не забыть это сделать (иначе последствия непредсказуемы), а если я забыл то надо мне как то об этом напомнить.

Наверное в модуле стоило бы проверять входные значения? И, если что-то с ними неправильно (их не задано при вызове модуля), то либо задавать некие дефолтные, определяемые в модуле, либо запрашивать у пользователя или просто вываливаться с сообщением об ошибке типа «Параметры задал? Нет? Отдыхай.». Пусть расчётный модуль этим и занимается перед расчётами.

Moisha_Liberman ★★
()
Ответ на: комментарий от AIv

как то специально обработать 2 меняющихся параметра

Тогда так:

m = calc.control(CPPModel(), "a,b") # здесь a и b меняются (и из командной строки не берутся), остальные постоянные

остальной код неизменен.

если я забыл то надо мне как то об этом напомнить

Ну тогда действительно считай количество изменений. Если больше 1, то исключение.

monk ★★★★★
()
Ответ на: Простите, ТС. от Moisha_Liberman

Прощаю

«В огороде бузина, в Киеве дядька»(с).

extending наск. я понимаю может заменить SWIG, но к обсуждаемой теме не имеет отношения практ. никакого.

Объясните на интуитивно-понятном уровне (на пальцах)?

Более понятно чем уже написано в треде увы не смогу.

AIv ★★★★★
() автор топика
Ответ на: А. Уже понятнее. от Moisha_Liberman

Наверное в модуле стоило бы проверять входные значения?

У него обратная задача. В модуле значения есть всегда (во время тестов использовались) и надо не меняя (или минимально меняя) модуль заменить параметры заданными в командной строке.

monk ★★★★★
()
Ответ на: комментарий от monk

Вооот... ну да, я примерно так и думал;-)

Есть какие то аналоги (идеологические), не обязательно в питоне?

AIv ★★★★★
() автор топика
Ответ на: Прощаю от AIv

Спасибо. :)

Я написал исходя из опыта своих решений. :) Именно потому что:

extending наск. я понимаю может заменить SWIG

:) Мне (в своё время) пришлось поломать голову каким путём идти — SWIG или не мудровать и остаться на extensions. По сути дела, это то, что «доктора питона» нам и прописали для расширения инфраструктуры питона своими модулями, если уж расширения прямо в документации к питону и описаны.

Я, поверьте, ни коим образом не собирался критиковать Ваш подход, я просто намекнул на то, что возможно имело бы смысл глянуть в строну расширений. :)

Moisha_Liberman ★★
()
Ответ на: комментарий от monk

Да, я это понял. :)

Здесь (на мой взгляд) вопрос в том, как это проще/эффективнее сделать. Вы написали о проксировании, но эта идея не кажется мне ни простой ни, как следствие, эффективной. :)

Moisha_Liberman ★★
()
Ответ на: Спасибо. :) от Moisha_Liberman

SWIG мне нравится тем что там ВООБЩЕ ничего не надо делать. У меня шаблонный макефайл, и дальше все взлетает само (за исключением каких то извратов - но и там нужны минимальные телодвижения). А в остальных решениях приходиться что то доп. прописывать.

AIv ★★★★★
() автор топика
Ответ на: Да, я это понял. :) от Moisha_Liberman

но эта идея не кажется мне ни простой ни, как следствие, эффективной. :)

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

Есть ещё вариант написать свой интерпретатор, но это ничуть не проще :-)

Был бы лисп, можно было бы написать макрос — чуть сложнее, чем прокси, но проще, чем интерпретатор.

monk ★★★★★
()
Ответ на: комментарий от monk

Это наск. я понимаю про оболочку (то что calc.control возвращает)? Спасибо, буду теперь знать как оно зовется;-)

Я имел ввиду подход, при котором существующий код рассматривается как шаблон кода (допускающий неявные изменения при запуске). Хотя наверное это тоже можно рассматривать как прокси...

AIv ★★★★★
() автор топика
Ответ на: комментарий от monk

Ты сейчас придумал геттеры-сеттеры а-ля Джава. getA/setA etc. Ничем не понятнее.

Virtuos86 ★★★★★
()
Ответ на: комментарий от AIv

Да. В части простоты...

... использования согласен. Я примерно так же решил проблему написания расширений через самопальные шаблоны. Просто с питоном беда — он может в принципе всё, но если нужна скорость, то всё печально.

Moisha_Liberman ★★
()
Ответ на: комментарий от ebantrop

Великая теорема Лавсана номер 3:

1) все разумные идеи реализованные в любом ЯП уже были реализованы в лиспе.

2) те идеи которые не были реализованы в лиспе ненужны.

AIv ★★★★★
() автор топика
Ответ на: Да. В части простоты... от Moisha_Liberman

Ну в момей области декомпозиция очень четкая - то что требует сокрости прекрасно пищется на плюсах, то что требует высокой скорости разработки и частых модификаций скорости не требует и прекрасно пишется на питоне.

AIv ★★★★★
() автор топика

Переписать на Cython :) Серьезно, сам так делал, когда нужно было оптимизировать критичные участки кода (область — обработка изображений в реалтайме)

buddhist ★★★★★
()
Ответ на: комментарий от qnikst

Да бл...ин!!!! Лор такой лор...

Нет, мне не нужен аналог gflag, getopt и пр - во первых у меня уже есть такой аналог, во вторых вопрос воообще не об этом.

AIv ★★★★★
() автор топика
Ответ на: комментарий от buddhist

И Cython мне не нужен. И C#. И подарочный набор ножей. И я не хочу поговорить о Господе нашем Иисусе;-)

AIv ★★★★★
() автор топика
Ответ на: комментарий от monk

Ну зачем тут прокси?

Давайте возьмём расширение для примера, чтоб по-короче как-то.

Чтобы долго не рассусоливать пусть будет вот это — http://www.dalkescientific.com/writings/NBN/c_extensions.html

Я даже не смотрю сейчас запустится али нет, правильно там всё с тояки зрения синтаксиса или что-то надо править. Нам не это важно, нам важен пример.

Там рабочая лошадка это ф-я iterate_point:

int iterate_point(double x0, double y0, int max_iterations) {
	int iteration = 0;
	double x=x0, y=y0, x2=x*x, y2=y*y;
	
	while (x2+y2<4 && iteration<max_iterations) {
		y = 2*x*y + y0;
		x = x2-y2 + x0;
		x2 = x*x;
		y2 = y*y;
		iteration++;
	}
	return iteration;
}

Очевидно что передаваемые в неё аргументы берутся откуда-то из питоновского кода. Ну тогда можно было бы, наверное как-то так:

if (x0 == 0)
    x0 = some_shit;
if (y0 == 0)
    y0 = some_outher_shit;
if (max_iterations == 0)
    max_iterations = some_different_shit;
Понятно что то, что там написано про shit* это некие осмысленные в данном случае значения. Не просто от балды взяли, то есть.

Ну или так:

if ((x0 == 0) || (y0 == 0) || (max_iterations == 0))
   fprintf(stderr, "Всё плёхо! Параметры забыли задать!\n");

Точно так же, можно было бы сделать разумную инициализацию (по сути дела проверку передаваемых аргументов и, при необходимости, их инициализации некими разумными параметрами) в самом питоне, при вызове данной ф-ии. Это не проще было бы?

Moisha_Liberman ★★
()
Ответ на: комментарий от AIv

Да. Такая же проблема.

Эти недостатки питона мне волею случая стали хорошо знакомы когда я с ним связался. :)))

Moisha_Liberman ★★
()
Ответ на: комментарий от AIv

LISP?

Кто-то сказал здесь «LISP»?!?

Всё-всё-всё... Пойду-ка я отсюда. Тред становится опасным. :) Прошу прощения за вмешательство...

Moisha_Liberman ★★
()
Ответ на: Ну зачем тут прокси? от Moisha_Liberman

На Вашем примере - вот исходный код:

from NCBC import *
print iterate_point(2.5, 0.34, 100)

А теперь я хочу ПОЧТИ НЕ МЕНЯЯ исходного кода запустьа из командной строки

./run.py x0=3.5
./run.py y0=.37 N=200

и т.д.

AIv ★★★★★
() автор топика
Последнее исправление: AIv (всего исправлений: 1)
Ответ на: комментарий от buddhist

И что? Это повод писать на всяком г-не?;-)

У меня только прототипы и ничего кроме прототипов. Вся работа ведется на прототипах. Иногда прототип называется релизом, передается заказчику и работа идет дальше;-)

AIv ★★★★★
() автор топика
Ответ на: Вот тут вариант от Moisha_Liberman

Я видел. Нет, это НЕПРАВИЛЬНЫЙ вариант;-) Ну то есть я так никогда не делаю, я сразу по дефолту ставлю актуальные параметры (и всем советую).

AIv ★★★★★
() автор топика
Ответ на: комментарий от AIv

В моём случае

нужно да, просто подумать какие параметры поставить в модуле чтобы не скатить дефолтное решение в гуано. Но, как правило, они хотя бы приблизительно известны из постановки задачи. Зато решение получается довольно элегантным. С одной стороны мы не забиваем код на питоне ненужными сущностями, с другой — модуль может сам решать в определённых пределах что и как нужно считать, основываясь на вызове.

Moisha_Liberman ★★
()
Ответ на: комментарий от AIv

Меня не звали, вот я чушь и отвечаю. Вопрос о переопределение части параметров из ком строки, и запрете другой части, при этом с минимальными изменениями в проект, так вот если ты осилил прочитать описание, то gflags позволяют это. И могу поспорить аналога у тебя нет

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

qnikst ★★★★★
()
Последнее исправление: qnikst (всего исправлений: 1)
Ответ на: комментарий от AIv

Нет, мне не нужен аналог gflag, getopt и пр

И Cython мне не нужен. И C#. И подарочный набор ножей. И я не хочу поговорить о Господе нашем Иисусе;-)

По-моему, тебе нужен аналог CSS на рефлексии.

tailgunner ★★★★★
()
Ответ на: комментарий от AIv

Я имел ввиду подход, при котором существующий код рассматривается как шаблон кода (допускающий неявные изменения при запуске)

https://ru.wikipedia.org/wiki/Monkey_patch

В CL почти основной метод расширения. Правда там более радикально:

from model import *
m = CPPModel()

превращалось бы в

from model import *
import calc # при загрузке меняет model.CPPModel

m = CPPModel() # здесь уже совсем другая функция
monk ★★★★★
()
Ответ на: комментарий от Virtuos86

ему нужно что-то вроде свойств из Qt QML

Я ничего не знаю о Qt, но, судя по терминам, это тот же CSS, вид в профиль.

Собственно, абстрактная идея в том, чтобы создать некую дефолтную модель, а потом применить к ней иерархический набор настроек.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от qnikst

Поспорить то ты конечно можешь, но С++ без извратов умеет интропсекцию? А питон умеет.

У меня есть биндинг произвольного питоньего класса в шелл, и в плане разбора ком.строки мне больше ничего не нужно.

Кроме того, я выше мельком писал - не обязаьельно параметры приходят из ком.строки, они могу прийти из другого места.

AIv ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.