Добавление функций y (x) вместе в python — невозможно?

#python #python-3.x #function #math #lambda

#python #python-3.x #функция #математика #лямбда

Вопрос:

В настоящее время я пытаюсь решить инженерную задачу, в которой мне нужно решить относительно длинные дифференциальные уравнения, для чего я использую метод odeint от scipy. Я изменил свою задачу на более простые переменные и уравнения, чтобы сократить приведенный ниже код и сделать его более понятным.

Я хочу добавить две части функции, здесь f и g , чтобы построить мое окончательное уравнение dydt . Обе части имеют одинаковые переменные y,x и другие разные постоянные аргументы.

Я получаю сообщение об ошибке «неподдерживаемые типы операндов для : ‘function’ и ‘function'».

Есть ли способ обойти это? Я предполагаю, что мог бы попытаться использовать lambda , но я довольно новичок в python и не мог найти способ заставить его работать.

 from scipy.integrate import odeint  
  
def f(y, x, arg1, arg2):
        f_result = y   x   arg1 * arg2
        return f_result

def g(y, x, arg3, arg4, arg5):
        g_result = y * x * (arg3   arg4   arg5)
        return g_result

def equation(fun1, fun2):
        dydt = fun1   fun2
        return dydt

y0, x_span, arg1, arg2, arg3, arg4, arg5 = 0, [x for x in range(11)] , 1, 2, 3, 4, 5

f_fun = f
g_fun = g
dydt = equation(f_fun, g_fun)

sol = odeint(dydt, y0, x_span, args=(arg1, arg2, arg3, arg4, arg5))
 

Комментарии:

1. Привет! Не могли бы вы показать нам, как будет выглядеть dydt? Это просто f(y, x, arg1, arg2) g(y, x, arg3, arg4, arg5) или результат, который вы ожидаете, более сложный, чем этот?

2. Неважно, я посмотрел немного больше в документации odeint. Собираюсь привести вам несколько примеров.

3. Реальная функция немного сложнее, но идея та же — есть одна независимая переменная (x), одна зависимая переменная (y) и несколько констант. Итак, в случае приведенного выше кода dydt будет просто dydt = y x (arg1 * arg2) y * x * (arg3 arg4 arg5) . Тогда метод odeint решит для y

4. Я предполагаю, что суть проблемы заключается в том, чтобы узнать, возможно ли объединить две функции, а затем решить результирующую функцию с помощью метода… Я мог бы просто написать их оба вместе, как я сделал выше, но код был бы гораздо более адаптируемым к другим подобным проблемам, если бы я мог разделить разные части основного уравнения для решения в отдельных функциях, как здесь f и g или больше . Спасибо за вашу помощь!!

5. Хм, я опубликовал первый ответ, но я понимаю, что это не то, что вы спросили сейчас. Позвольте мне поискать еще немного.

Ответ №1:

Вы можете создавать такие вещи, но это будет не очень легко поддерживать. Ваша проблема здесь в том, что вы хотите вернуть функцию из функции. Вы можете легко сделать это в python, потому что вы можете определить функцию внутри одного и вернуть ее.

Сложность заключается в том, что ваши «внутренние» функции будут каждый раз требовать разных аргументов, поэтому управлять динамическим созданием другой функции, которая будет иметь правильную подпись… Ну, как вы увидите, это не так сложно, просто не обязательно будет самым простым в обслуживании.

Очень простой способ сделать то, что вы хотите, это:

 from scipy.integrate import odeint

def f(y, x, arg1, arg2):
    f_result = y   x   arg1 * arg2
    return f_result

def g(y, x, arg3, arg4, arg5):
    g_result = y * x * (arg3   arg4   arg5)
    return g_result

def equation(fun1, fun2):
    def inner(*args):
        y = args[0]
        x = args[1]
        arg1 = args[2]
        arg2 = args[3]
        arg3 = args[4]
        arg4 = args[5]
        arg5 = args[6]
        dydt = fun1(x, y, arg1, arg2)   fun2(x, y, arg3, arg4, arg5)
        return dydt
    return inner

y0, x_span, arg1, arg2, arg3, arg4, arg5 = 0, [x for x in range(11)], 1, 2, 3, 4, 5

f_fun = f
g_fun = g
dydt = equation(f_fun, g_fun)

sol = odeint(dydt, y0, x_span, args=(arg1, arg2, arg3, arg4, arg5))
 

inner принимает *args в качестве аргументов, что означает «любое количество аргументов в виде списка». Как вы можете видеть, я уточнил, как извлечь из него каждое значение, чтобы перестроить нашу функцию.

Здесь это действительно грубо и просто. Если вы действительно хотите сделать его немного более разрешительным, вы можете попытаться угадать количество аргументов, которые требуются вашим функциям f , и g проверить их, чтобы f и g могли немного измениться.

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

 from scipy.integrate import odeint
from inspect import signature

def f(y, x, arg1, arg2):
    f_result = y   x   arg1 * arg2
    return f_result

def g(y, x, arg3, arg4, arg5):
    g_result = y * x * (arg3   arg4   arg5)
    return g_result

def equation(fun1, fun2):
    def inner(*args):
        y = args[0]
        x = args[1]
        fun_args = (x for x in list(args[2:]))
        fun1_args = [next(fun_args) for _ in range(len(signature(fun1).parameters) - 2)] # We remove 2 because y and x are always there
        fun2_args = [next(fun_args) for _ in range(len(signature(fun2).parameters) - 2)]
        dydt = fun1(x, y, *fun1_args)   fun2(x, y, *fun2_args)
        return dydt
    return inner

y0, x_span, arg1, arg2, arg3, arg4, arg5 = 0, [x for x in range(11)], 1, 2, 3, 4, 5

f_fun = f
g_fun = g
dydt = equation(f_fun, g_fun)

sol = odeint(dydt, y0, x_span, args=(arg1, arg2, arg3, arg4, arg5))
 

И для хорошей оценки вы могли бы даже решить выполнить произвольное количество функций, если вы всегда размещаете параметры в правильном порядке!

 from scipy.integrate import odeint
from inspect import signature

def f(y, x, arg1, arg2):
    f_result = y   x   arg1 * arg2
    return f_result

def g(y, x, arg3, arg4, arg5):
    g_result = y * x * (arg3   arg4   arg5)
    return g_result

def equation(*args):
    def inner(*inner_args):
        y = inner_args[0]
        x = inner_args[1]
        dydt = []
        fun_args = (x for x in list(inner_args[2:]))
        for func in args:
            func_args = [next(fun_args) for _ in range(len(signature(func).parameters) - 2)] # We remove 2 because y and x are always there
            dydt.extend(func(x, y, *func_args))
        return sum(dydt)
    return inner

y0, x_span, arg1, arg2, arg3, arg4, arg5 = 0, [x for x in range(11)], 1, 2, 3, 4, 5

f_fun = f
g_fun = g
dydt = equation(f_fun, g_fun)

sol = odeint(dydt, y0, x_span, args=(arg1, arg2, arg3, arg4, arg5))
 

Теперь возникает реальный вопрос: действительно ли это более удобно для обслуживания таким образом?

Вот как я бы это сделал лично, допуская некоторые быстрые изменения, но это слишком сложно.

 def equation(y, x, *fun_args):
    def f():
        arg1, arg2 = *fun_args[0]
        f_result = y   x   arg1 * arg2
        return f_result

    def g():
        arg3, arg4, arg5 = *fun_args[1]
        g_result = y * x * (arg3   arg4   arg5)
        return g_result

    return f()   g()

y0, x_span, arg1, arg2, arg3, arg4, arg5 = 0, [x for x in range(11)], 1, 2, 3, 4, 5

sol = odeint(equation, y0, x_span, args=((arg1, arg2), (arg3, arg4, arg5)))
 

Это позволяет мне играть только с внутренней частью функции «equation», если я передаю правильные аргументы sol = odeint(equation, y0, x_span, args=((arg1, arg2), (arg3, arg4, arg5))) . Я также перегруппировал параметры функции в tuple для лучшей видимости.

Комментарии:

1. Вау, в этом есть большой смысл! Я думал о том, чтобы как-то поместить параметры в отдельную переменную, но не знал как. Теперь я понимаю! Спасибо за объяснение, я действительно оценил это!

2. Рад помочь 🙂 Не забудьте принять ответ, если этого достаточно для вас, чтобы как бы «закрыть» вопрос.

3. Извините! только что сделал это сейчас 🙂 Я пока не могу проголосовать, но я поставил зеленую галочку!