Найти - Пользователи
Полная версия: как пересоздать класс? (или кое-какие вопросы по стеку)
Начало » Python для новичков » как пересоздать класс? (или кое-какие вопросы по стеку)
1
Levitanus
В общем, у меня очень расплывчатый и обширный вопрос, поэтому начну с use-case, хотя мне в принципе интересно разобраться в механике реализации системы импортов и создания объектов в реалтайме.

UseCase:
я все еще пишу “транслятор кода\препроцессор” (надеюсь на дня закончить) для проприеритарной среды разработки.
Т.к. основная претензия к текущему положению вещей у меня в отсутствии возможностей для инкапсуляции и полном отсутствии инструментов модульных тестов, иду не по пути парсинга исходников, а поддерживаю полностью живые объекты, чтобы можно было гонять тесты на питоне, не заходя в среду назначения.
Поэтому генерация кода идет по принципу логирования.
Допустим, при запуске вне процесса компиляции (в тестах), этот код ведет себя вполне по-человечьи:
 arrY = kArrInt(name='arrY', sequence=[1, 2, 3, 6])
y = kInt(name='y')
break_indicies = [0, 2, 3, 3, 3]
with For(arr=self.arrX) as seq:
    for idx, val in enumerate(seq):
        self.x <<= val
        with For(arr=arrY) as seq_y:
            for idx2, val2 in enumerate(seq_y):
                with If(self.x == val2):
                    check()
                    y <<= val2
                    with self.subTest():
                        self.assertEqual(
                            idx2, break_indicies[idx])
                with Else():
                    check()
                    Break()
а во время компиляции выдает уже полноценное дерево:
inc($_for_loop_curr_idx)
%_for_loop_idx[$_for_loop_curr_idx] := 0
while(%_for_loop_idx[$_for_loop_curr_idx] < 5)
$x := %arrX[%_for_loop_idx[$_for_loop_curr_idx]]
inc($_for_loop_curr_idx)
%_for_loop_idx[$_for_loop_curr_idx] := 0
while(%_for_loop_idx[$_for_loop_curr_idx] < 4)
if($x = %arrY[%_for_loop_idx[$_for_loop_curr_idx]])
$y := %arrY[%_for_loop_idx[$_for_loop_curr_idx]]
else
%_for_loop_idx[$_for_loop_curr_idx] := 4
end if
inc(%_for_loop_idx[$_for_loop_curr_idx])
end while
dec($_for_loop_curr_idx)
inc(%_for_loop_idx[$_for_loop_curr_idx])
end while
dec($_for_loop_curr_idx)
Ну и для того, чтоб у меня не падали тесты от учета лишних объектов, лежащих в логе, я для каждого логгера написал простые refresh методы, которые приводят их к исходному состоянию. Все отлично.

Аднака, все куда сложнее, когда дело касается вызываемых объектов, их там кот наплакал: каллбеки и функции, но они есть, и сделаны с помощью декораторов, которые эти функции добавляют в лог, а на этапе генерации кода разворачивают.
Типа так:
 @func
def method(self, arg=kOut(int)):
    self.assertEqual(
        arg.name(),
        '%_stack_functions_int_arr[0 + %_stack_functions_int_idx' +
        '[$_stack_functions_int_pointer]]')
    arg <<= self.int
def test_funcs(self):
    @func
    def foo(loc: kArg(int, 5)=arr):
        with For(arr=loc) as seq:
            for item in seq:
                self.method(item)
    foo()
    # test for excluding from generation without
    # "calling" it
    @func
    def bar(var: int):
        self.method()
    bar(1, inline=True)
    generated_exec = KspObject.generate_all_executables()
    self.maxDiff = None
    self.assertEqual(
        unpack_lines(generated_exec),
        executables.format(method=Function.get_func_name(self.method),
                           foo=Function.get_func_name(foo)))
И с функциями тоже все норм, т.к. когда они вызываются, логгер функций их хеширует, и будут генериться только те, которые вызваны после рефреша.

Но вот каллбеки вызывать прямо как-то противоестесственно. Да и не понятно где.
вот такое их использование чисто синтаксически как раз само то:
 init_lines = \
    '''on init
foo_line
bar_line
cb body is 4
end on'''
release_lines = \
    '''on release
release
end on'''
class TestWrapers(DevTest):
    def setUp(self):
        Output().refresh()
        self.var = 4
    def cb(self):
        Output().put(f'cb body is {self.var}')
        self.var += 1
    def runTest(self):
        @init
        def foo():
            Output().put('foo_line')
        @init
        def bar():
            Output().put('bar_line')
        init(self.cb)
        self.assertEqual(self.var, 4)
        self.assertEqual(
            unpack_lines(InitCallback.generate_body()), init_lines)
        self.assertEqual(self.var, 5)
        @release
        def rls():
            Output().put('release')
        self.assertEqual(
            unpack_lines(ReleaseCallback.generate_body()), release_lines)
        rls()
        self.assertEqual(Output().pop(), 'release')
И здесь возникает конфликт с тем, что декораторы каллбеков автоматически добавляют эти функции в логгеры каллбеков. И делают они это один раз. Получается, что если писать гипотетический рефреш, он будет просто удалять все это функции, вне зависимости от времени выполнения. И с этим еще можно смириться на этапе тестирования пакета, хотя не хотелось бы.
Но я бы хотел сохранить возможность пакетной генерации скриптов, причем, желательно, не вылезая для этого в батники, а делать это средствами питона.
Допустим, есть два объекта типа Script, в функциях main которого и инициализируются все необходимые скрипты классы. Ну и при компиляции они, соответственно, генерируют код, а потом скрипт рефрешит классы к начальному состоянию, можно компилировать следующий.
Вот такой схеме оч мешает наличие декораторов… Можно ли как-то избежать их, не теряя выразительности, или каким-то макаром “переимпортировать” модули, чтобы заново создавались все объекты, присутствующие в них?
Levitanus
приплыли.
Оказывается, DevTest в таком импорте
  path = os.path.abspath(os.path.dirname(__file__)) + '/..'
sys.path.append(path)
path = os.path.abspath(os.path.dirname(__file__))
sys.path.append(path)
from mytests import DevTest
вообще ни разу не DevTest в таком:
 from pyksp.compiler.tests.mytests import DevTest
И как дальше жить?
doza_and
Levitanus
И как дальше жить?
Levitanus
очень расплывчатый и обширный вопрос
Очевидно что избавиться от расплывчатости.
А например вообще не понял в чем проблема.

Что за среда разработки? транслятор с какого языка на какой? Зачем тут вообще кодогенерация? Каким боком тут питон появился? Объекты в питоне и большинстве языков почти всегда в реалтайме создаются, чего тут удивительного? И т.д. и т.п….

Напишите попонятнее.
Rodegast
> я все еще пишу “транслятор кода\препроцессор” (надеюсь на дня закончить) для проприеритарной среды разработки.

Что за среда?
Levitanus
doza_and
Что за среда разработки?
я вот так вот ссылку на мануалы брошу, в одном в оглавлении есть раздел “что это за птица”, а второй - актуальный референс по языку
https://yadi.sk/d/Wj6VuO-W4NQA2g

doza_and
транслятор с какого языка на какой?
с питона на KSP (язык, на котором пишутся скрипты в среде)
doza_and
Зачем тут вообще кодогенерация?
потому что без нормального препроцессора писать на KSP – боль ужасная.
doza_and
Каким боком тут питон появился?
Вообще изначально – потому что тот кодогенератор, которым комьюнити польуется сейчас – написан на питоне. Хотел сначала форкнуть его и допиливать под себя, а в процессе решил вообще от него отказаться, и писать с нуля, и без парсинга.
doza_and
Объекты в питоне и большинстве языков почти всегда в реалтайме создаются, чего тут удивительного?
да. Только объекты модуля создаются при импорте, в чем и была суть вопроса.
Rodegast
> я вот так вот ссылку на мануалы брошу

Твои мануалы написаны на не понятном языке.
Levitanus
Rodegast
Твои мануалы написаны на не понятном языке.
эмм… вроде обычный технический англицкий)

среда – ромплер, который играет аудиосэмплы. Типа синтезатора, только не моделирует звук, а проигрывает уже записанное.
Работает как стенделон, так и как VST-плагин (это стандарт для плагинов к аудио хостам). Выполняет роль прослойки между разработчиком и процессом написания движка проигрывания сэмплов, что обычно делается на сях.
А в самом движке “продукты” (или проекты) делятся на инструменты, содержащие в себе сэмплы, и управляемые скриптами на этом самом KSP.

Язык добавили уже к третьей версии ромплера, так что киллер-фичей он никогда не был, но в итоге без него никак.
JOHN_16
Levitanus
эмм… вроде обычный технический англицкий)
просто товарищ Rodegast везде где можно бьет кулаком в грудь и кричит что английский не нужен, поэтому не обращай внимания.

P.S. мммм NI, где же мои годы назад, когда я во всем этом варился..правка как пользователь, а не разработчик. Ну да ладно.
Levitanus
JOHN_16
P.S. мммм NI

JOHN_16
английский не нужен
ой, я сколько ржал над идеей, что надо учить китайский, бо все на нем говорить будем) А теперь реально надо за полгода осилить)

Возвращаясь к теме:
можно ли как-то тот факт, что объект, импортированный из переменной Path, и импортированный релативным импортом изнутри пакета не один и тот же, использовать для пересоздания декораторов, валидных только для одного скрипта, но не для другого?
Допустим, если скрипты будут находится в файлах script1.py и script2.py,
в каждом из которых будут прописанные необходимые только конкретному скрипту импорты, а батч-компилятор сначала импортирует, скажем, один файл, а потом другой?
Только чтоб оформлять это не громоздкими конструкциями из cmd, и не заставлять пользователя напрямую юать importlib, а как-нибудь цивильнее?
Levitanus
короче, пришел я к такому грязноватому решению:

module: simple_test
 class Test:
    count = 0
module: simple_test2
 from simple_test import Test
class Script1:
    Test.count += 1
    print(Test.count)
module: simple_test3
 from simple_test import Test
class Script2:
    Test.count += 1
    print(Test.count)
module: simple_test4
 from simple_test2 import Script1
from simple_test3 import Script2
from simple_test import Test
import importlib
import importlib.util
Test.count = 0
class Runner:
    def __init__(self, cls):
        spec = importlib.util.find_spec(cls.__module__)
        mod = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(mod)
a = Runner(Script1)
b = Runner(Script2)
 1
2
1
2
[Finished in 0.1s]
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB