Q:

Returning automatic derivatives with Sympy

I'm trying to build a class whose goal is returning the derivative of a function f also as a function. I've read about Sympy and I started to try it with this package.
Main problem
Let's suppose that I have a simple function with only one parameter, like this:
def f1p(x):
    return x**2 + 5**x * 2*x + 1

Now, I have a method that ensures the funcion has one only paramether and then calculates the derivative (only as expression):
from sympy import *
import inspect
def get_derivative(fun):
    parameters=inspect.getargspec(f).args
    if(len(parameters)>1):
       raise ValueError('Function has more than one parameter.')
    fdiff=fun(Symbol(parameters[0])).diff()
    print(fdiff)

Let's say that fdiff contains the main expression of derivative function, so, the problem I want to solve is returning a function in order to evaluate it, for example:
f_deriv=get_derivative(f1p) #f_deriv is a callable function 
print(f_deriv(a)) #Prints derivative value of f1p in a

Note: I've try return eval/exec(diff) but is a bad idea, because declaring parameters is needed. Also I tried to wrap the expresion with:
eval('def foo('+parameters[0]+'):\n\treturn '+diff)

And it still is not a good idea.
Extending main problem
Suppose now we use (for example) logarithms in our base function, so, we implement it this way
import math
def f1p(x):
    return x**2 + 5**x * 2*x + 1 + math.log(x)

When I apply the previous function to this, I get the following error:
TypeError: can't convert expression to float

This is due to Sympy is not capable of understand math.log(x). So, my questions are:

To solve main problem: Is there any way to return a callable function from get_derivative function?
To solve extended problem: Is there any way to translate sympy expression to Python evaluable expression?

Thanks in advance.

A:

You could use lambdify to return callable from your get_derivative:
import inspect
from sympy import symbols, diff
from sympy.utilities.lambdify import lambdify

def get_derivative(function):
    if len(inspect.getfullargspec(function).args) > 1:
        raise ValueError('Function has more than one parameter.')
    x = symbols('x')
    return lambdify(x, diff(f1p(x), x))

Checking result for your first f1p version:
def f1p(x):
    return x**2 + 5**x * 2*x + 1

derivative = get_derivative(f1p)
derivative(1)

This will give 28.094379124341003. It's a correct result.

Now, if you want to use logarithm, then you should use the one from sympy, not from math:
from sympy import log
def f1p(x):
    return x**2 + 5**x * 2*x + 1 + log(x)

derivative = get_derivative(f1p)
derivative(1)

This will give 29.094379124341003. Also correct.

