How to add a callable function to a Sympy expression - python

If I have an expression x = Symbol('x') and f1=x**2 and I want to further add some f2 where f2 = interp1d(t, y) is scipy interpolation. How does one turn f2 into an expression such that I have somthing like f = x**2 + f2(x) so that I can later evaluate f as f.subs(x, some_number)?
Due to specification of the code, I can't evaluate f1 and f2 separatelly and then add the resulting numbers, I need to be able to add it to an existing sympy expression and evaluate it using something like .subs()

One way but it requires hard-coding the function to be called in the class:
f2 = lambda t: np.sin(t)
class MyFunc(Function):
#classmethod
def eval(cls, arg):
arg = sympify(arg, strict=True)
if arg.is_Number:
return sympify(f2(float(arg)), strict=True)
More like Davide's answer but with a couple of fixes:
class FuncWrapper(Symbol):
"""Wraps a python callable as a Basic instance"""
def __new__(cls, func, name):
obj = super().__new__(cls, name)
obj._wrapped = func
return obj
#property
def wrapped(self):
return self._wrapped
def _hashable_content(self):
return (self.wrapped,) # needed for __eq__
def eval(self, arg):
if arg.is_Number:
return sympify(self.wrapped(float(arg)))
def __call__(self, arg):
return Call(self, arg)
class Call(Function):
#classmethod
def eval(cls, func, arg):
arg = sympify(arg)
result = func.eval(arg)
if result is not None:
return result
With that you have:
In [61]: f = FuncWrapper(np.sin, 'f')
In [62]: x + f(x)
Out[62]: x + Call(f, x)
In [63]: _.subs(x, 1)
Out[63]: 1.84147098480790

One very risky way would be to create a wrapper object for your numerical function, like this:
from sympy import *
import numpy as np
var("x")
# symbolic expression
f1 = cos(x)
# numerical function
f2 = lambda t: np.sin(t)
class MyFunc(Expr):
"""Create a symbolic wrapper to a numerical function."""
def __new__(cls, arg, **kwargs):
obj = Expr.__new__(cls, **kwargs)
obj._custom_func = arg
return obj
def _subs(self, old, new, **hints):
return self._custom_func(float(new))
expr = f1 + MyFunc(f2)
expr.subs(x, np.pi/4)
# out: 1.41421356237309

Related

Possible in Python - Class method that can retrieve instance if called from instance instead of class?

Thinking about this and I'm wondering if it is possible (and if so, how to make such a decorator etc.) to have a classmethod, that IF called from an instance, can retrieve data on the instance? Perhaps some more clarity on how the staticmethod and classmethod decorators work would be helpful too (looking at the implementation __builtin__.py did not help)
Example use would be:
class A(object):
def __init__(self, y):
self.y = y
#classmethod
def f(cls, x, y=None):
# if y is unspecified, retrieve it from cls which is presumably an instance
# (should throw an error if its a class because y is not set
if y is None:
y = cls.y
return x + y
So that we could do:
>>>A.f(3, 5)
8
>>>a = A(5)
>>>a.f(3)
8
I came up with this below to mimic the behavior but its pretty inconvenient to implement:
class A(object):
def __init__(self, y):
self.y = y
self.f = self.f_
def f_(self, x):
return x + self.y
#classmethod
def f(cls, x, y):
return x + y
To expand on the comments made by #Adirio You could make a decorator that accomplishes this dynamically.
In this particular implementation, when the decorated method is called it will do a partial bind of the provided arguments to the method and uses the method's signature to determine what parameters have not been provided.
For any unspecified argument, if the calling object has an attribute matching the unspecified parameter name, the object's attribute value will be injected into the function.
import inspect
class BindableConstructor(object):
def __init__(self, meth):
self.meth = meth
self.sig = inspect.signature(self.meth)
def __get__(self, obj, klass=None):
if obj is not None:
print('Method ', repr(self.meth), ' called from instance ', repr(obj))
if klass is None:
klass = type(obj)
def newmeth(*args, **kwargs):
ba = self.sig.bind_partial(*args, **kwargs)
ba.apply_defaults()
for paramname in self.sig.parameters:
if paramname not in ba.arguments and hasattr(obj, paramname):
ba.arguments[paramname] = getattr(obj, paramname)
return self.meth(klass, *ba.args, **ba.kwargs)
return newmeth
Then suppose you have the following class using this decorator
class MyClass(object):
def __init__(self, y):
self.y = y
#BindableConstructor
def my_constructor(cls, x, y):
return cls(x + y)
Then the following behavior would be observed
>>> a = MyClass(5)
>>> b = MyClass.my_constructor(3, 2)
>>> b
<__main__.MyClass object at 0x0605C770>
>>> b.y
5
>>> c = b.my_constructor(3) # c.y == b.y + 3
Method <function MyClass.my_constructor at 0x05396420> called from instance <__main__.MyClass object at 0x0605C770>
>>> c.y
8
In this particular case ba.apply_defaults is called before checking the object's attributes to inject. If you want the object's attributes to take precedence over defaults, call ba.apply_defaults after the parameter injection logic.
When you try you example, you get an error saying
AttributeError: type object 'A' has no attribute 'y', because in constructor, you assigned y as an attribute of the object and not of the class.
The trivial fix:
class A(object):
def __init__(self, y):
A.y = y
#classmethod
def f(cls, x, y=None):
# if y is unspecified, retrieve it from cls which is presumably an instance
# (should throw an error if its a class because y is not set
if y is None:
y = cls.y
return x + y
Would indeed solve the error, but as the class will only know one single object at a time, you would get weird result as soon as you use more than one:
>>> A.f(3,5)
8
>>> a = A(5)
>>> a.f(3) # fine till there...
8
>>> b = A(7)
>>> a.f(3) # last created object wins here!
10
So the only foolproof way is to create an attribute with the name of the class function in each object. As you only call a class method, a lamdba is enough:
class A(object):
def __init__(self, y):
self.y = y
self.f = lambda x: A.f(x, y) # declare a shortcut for the class method
#classmethod
def f(cls, x, y=None):
return x + y
You can then safely do:
>>> A.f(3,5)
8
>>> a = A(5)
>>> a.f(3)
8
>>> b = A(7)
>>> a.f(3)
8
>>> b.f(3)
10
Do not forget to handle error cases.
class InstanceAndClassMethod(object):
def __init__(self, f):
self.f = f
def __get__(self, instance, owner=None):
if instance is None:
instance = owner
def newfunc(*args, **kwargs):
return self.f(instance, *args, **kwargs)
return newfunc
class A(object):
def __init__(self, y):
self.y = y
#InstanceAndClassMethod
def f(cls, x, y=None):
try:
y = cls.y if y is None else y
except AttributeError:
raise TypeError("f() missing 1 required positional argument: 'y'")
return x + y
With the help of docs.python.org/3/howto/descriptor.html I came up with this, seems to work:
class CoolerClassMethod(object):
def __init__(self, f):
self.f = f
def __get__(self, obj, klass):
if obj is None:
self_ = klass
else:
self_ = obj
def newfunc(*args, **kwargs):
return self.f(self_, *args, **kwargs)
return newfunc
class A(object):
def __init__(self, y):
self.y = y
#CoolerClassMethod
def f(cls, x, y=None):
y = cls.y if y is None else y
return x + y
Testing:
>>> a = A(5)
>>> A.f(3, 5)
8
>>> a.f(3)
8
>>> A.f(3, 5)
8

self variable of class as an argument of a class method (python)

here's my problem:
given any two functions, eg. f(x,a) and g(x,b), I want to build a new function, say F(f,g), which returns the product of the f and g. So:
F(f,g) = f*g = f(x, a) * g(x, b) = F(x, a, b)
I want to do this hardcoding the least possible. So, for h(x, c, d), I would get F(f,h) = F(x, a, c, d).
Given that then I want to minimize F, I thought of building a class. Here's a MWE:
import numpy as np
from inspect import getargspec
def f(x, a):
return np.tanh(x*a)
def g(x, b):
return np.power(x,b)
def h(x, c, d):
return x*c+np.log(x)
class fit_func(object):
def __init__(self, data, *args):
self.data = data
self.func_a = args[0]
self.func_b = args[1]
self.args_a = getargspec(args[0])[0][1:]
self.args_b = getargspec(args[1])[0][1:]
at this point, I thought of including the following __call__ method:
def __call__(self, *self.args_a, *self.args_b):
return self.func_a(self.data,self.args_a)*self.func_b(data,self.args_b)
I thought: this way an instance of the class, say F = fit_func(some_data_array,f,g), would be callable as F(a,b). However, python doesn't like the self.args_a and self.args_b among the arguments of __call__ and I understand why.
Does anybody know a clever way to obtain this? Thank you very much in advance
If you just accept positional arguments you better to save the length of arguments for each functions, and then pas proper slices of args to each function in call method:
import numpy as np
from inspect import getargspec
class fit_func(object):
def __init__(self, *args):
self.func_a = args[0]
self.func_b = args[1]
self.size_arg_a = len(getargspec(self.func_a)[0])
self.size_arg_b = len(getargspec(self.func_b)[0])
def __call__(self, *args):
return self.func_a(*args[:self.size_arg_a]) * self.func_b(*args[self.size_arg_b-1:])
Demo:
def f(x, a):
return np.tanh(x*a)
def h(x, c, d):
return x*c+np.log(x)
F = fit_func(f, h)
print(F(3, 4, 3, 5, 7))
16.0986122875
If you want to pass keyword arguments to final function:
import numpy as np
from inspect import getargspec
from operator import itemgetter
class fit_func(object):
def __init__(self, *args):
self.func_a = args[0]
self.func_b = args[1]
self.arg_a = getargspec(self.func_a)[0]
self.arg_b = getargspec(self.func_b)[0]
def __call__(self, **kwargs):
arg_a = itemgetter(*self.arg_a)(kwargs)
arg_b = itemgetter(*self.arg_b)(kwargs)
return self.func_a(*arg_a) * self.func_b(*arg_b)
Demo:
def f(x, a):
return np.tanh(x*a)
def h(x, c, d):
return x*c+np.log(x)
F = fit_func(f, h)
print(F(x=3, a=4, c=5, d=7))
16.0986122875

Does functools.wraps have the undecorated reference?

#!/usr/bin/python
from functools import wraps
def logged(func):
#wraps(func)
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging
#logged
def f(x):
"""does some math"""
return x + x * x
I want to know if wraps has the undecorated reference to the function f? I don't see it when I tried dir(f)
Modified version
#!/usr/bin/python
from functools import wraps
def logged(func):
#wraps(func)
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
with_logging.undecorated = func
return with_logging
#logged
def f(x):
"""does some math"""
return x + x * x
f.undecorated
No attribute? I was merely following what I used to do with decorator...
There is a reference to the original f in there, but it's messy to get to.
>>> f
<function f at 0x023F6DF0>
>>> f.func_closure[0].cell_contents
<function f at 0x023F6E30>
The first is the wrapped f, the second is the original f, notice the hex addresses are different.
If you need access to the original f, I suggest you wrap it differently:
def f(x):
"""does some math"""
return x + x * x
original_f = f
f = logged(f)
Now you have f and original_f, both usable.
You can access the original function by calling:
f.__wrapped__()
where f is the function that you are decorating

Method overloading decorator

I'm trying to write a decorator that provides method overloading functionality to python, similar to the one mentioned in PEP 3124.
The decorator I wrote works great for regular functions, but I can't get it to work for methods in a class.
Here is the decorator:
class Overload(object):
def __init__(self, default):
self.default_function = default
self.type_map = {}
self.pos = None
def __call__(self, *args, **kwargs):
print self
try:
if self.pos is None:
pos = kwargs.get("pos", 0)
else:
pos = self.pos
print args, kwargs
return self.type_map[type(args[pos])](*args, **kwargs)
except KeyError:
return self.default_function(*args, **kwargs)
except IndexError:
return self.default_function(*args, **kwargs)
def overload(self, *d_type):
def wrapper(f):
for dt in d_type:
self.type_map[dt] = f
return self
return wrapper
When I attempt to implement it like this:
class MyClass(object):
def __init__(self):
self.some_instance_var = 1
#Overload
def print_first_item(self, x):
return x[0], self.some_instance_var
#print_first_item.overload(str)
def print_first_item(self, x):
return x.split()[0], self.some_instance_var
I get a TypeError when I run it:
>>> m = MyClass()
>>> m.print_first_item(1)
<__main__.Overload object at 0x2> (1,) {}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "overload.py", line 17, in __call__
return self.default_function(*args, **kwargs)
TypeError: print_first_item() takes exactly 2 arguments (1 given)
>>>
My question is: How can I access the instance of MyClass (i.e. self) from within the decorated method?
Essentially, your Overload class needs a __get__ method:
def __get__(self, obj, cls):
# Called on access of MyClass.print_first_item.
# We return a wrapper which calls our
print "get", self, obj, cls
if obj is None:
# a function would do some checks here, but we leave that.
return self
else:
return lambda *a, **k: self(obj, *a, **k)
Why?
Well, you use your Overload object as a kind of function replacement. You want it, like a function, to represent itself in a method context with different signature.
Short explanation how method access works:
object.meth(1, 2)
gets translated to
object.__dict__['meth'].__get__(object, type(object))(1, 2)
A function's __get__() returns a method object which wraps the function by prepending the object to the parameter list (where it results in self):
realmethod = object.__dict__['meth'].__get__(object, type(object))
realmethod(1, 2)
where realmethod is a method object which knows the function to be called and the self to be given to it and calls the "real" function appropriately by transforming the call into
meth(object, 1, 2)
.
This behaviour we imitate in this new __get__ method.
as abarnert says as you are using a class as your decorator 'self' is an instance of Overload rather than MyClass as you hope/expect.
I couldn't find a simple solution. The best thing I could come up with is not using a class as a decorator and instead use a function but with a second argument with a default of a dictionary. Since this is an mutable type it will be the same dictionary every time the function is called. I use this to store my 'class variables'. The rests folows a similar pattern to your solution.
Example:
import inspect
def overload(funcOrType, map={}, type=None):
if not inspect.isclass(funcOrType):
# We have a function so we are dealing with "#overload"
if(type):
map[type] = funcOrType
else:
map['default_function'] = funcOrType
else:
def overloadWithType(func):
return overload(func, map, funcOrType)
return overloadWithType
def doOverload(*args, **kwargs):
for type in [t for t in map.keys() if t != 'default_function'] :
if isinstance(args[1], type): # Note args[0] is 'self' i.e. MyClass instance.
return map[type](*args, **kwargs)
return map['default_function'](*args, **kwargs)
return doOverload
Then:
from overload import *
class MyClass(object):
def __init__(self):
self.some_instance_var = 1
#overload
def print_first_item(self, x):
return x[0], self.some_instance_var
#overload(str)
def print_first_item(self, x):
return x.split()[0], self.some_instance_var
m = MyClass()
print (m.print_first_item(['a','b','c']))
print (m.print_first_item("One Two Three"))
Yeilds:
('a', 1)
('One', 1)
For reference, here is the working implementation, thanks to the detailed explanation by glglgl:
argtype_tuple = lambda args: tuple(type(a) for a in args)
class Overload(object):
def __init__(self, func):
self.default = func
self.map = {}
def __call__(self, *args, **kwargs):
key_tuple = argtype_tuple(args)
c_inst = kwargs.pop("c_inst", None)
if c_inst:
args = (c_inst,) + args
try:
return self.map[key_tuple](*args, **kwargs)
except KeyError:
return self.default(*args, **kwargs)
def __get__(self, obj, cls):
if obj:
return lambda *args, **kwargs: self(c_inst=obj, *args, **kwargs)
else:
return self
def overload(self, *types):
def wrapper(f):
for type_seq in types:
if type(type_seq) == tuple:
type_seq = tuple(type_seq)
else:
type_seq = (type_seq,)
self.map[type_seq] = f
return self
return wrapper
#Some tests/usage examples
class A(object):
#Overload
def print_first(self, x):
return x[0]
#print_first.overload(str)
def p_first(self, x):
return x.split()[0]
def __repr__(self):
return "class A Instance"
a = A()
assert a.print_first([1,2,3]) == 1
assert a.print_first("one two three") == "one"
#Overload
def flatten(seq):
return [seq]
#flatten.overload(list, tuple)
def flat(seq):
return sum((flatten(item) for item in seq), [])
assert flatten([1,2,[3,4]]) == [1,2,3,4]
assert flat([1,2,[3,4]]) == [1,2,3,4]

Chaining decorators that are defined as classes?

For the sake of learning, I'm trying to chain decorators that are defined by classes. I read this question about decorators, which has a lot of good information about chaining them with functions. They also link to the documentation, but I'm trying to figure out simpler examples.
Basically, I'm trying to mimic similar behaviour using classes. Here is my first decorator definition, which works perfectly.
class square_result(object):
def __init__(self, f):
pass
def __call__(self, x, y):
return (x+y)**2
#square_result
def add_two_numbers(x, y):
return x + y
print(add_two_numbers(2,5)) #Outputs 49, as expected
Then, I add another decorator to create this code snippet:
class square_result(object):
def __init__(self, f):
pass
def __call__(self, x, y):
return (x+y)**2
class append_abc(object):
def __init__(self, f):
pass
def __call__(self, *args):
return str(*args) + "abc"
#append_abc
#square_result
def add_two_numbers(x, y):
return x + y
print(add_two_numbers(2,5))
#Ideally, this should print "49abc" but prints "(2,5)abc" instead
what is the proper way of doing this? I guess what I want to do is create a decorator in the form of a class that takes the output of the function it decorates (in this case square_result) and appends "abc" to it.
I know that when I add a decorator to my code, the add_two_numbers() function is compiled and that function object is passed to the square_result class, which does something to create a function-like object which is substituted for the original add_two_numbers(). However, I'm not sure how to chain this.
This does what you want:
class square_result(object):
def __init__(self, f):
pass
def __call__(self, x, y):
return (x+y)**2
class append_abc(object):
def __init__(self, f):
self.f = f
def __call__(self, *args):
return str(self.f(*args)) + "abc"
#append_abc
#square_result
def add_two_numbers(x, y):
return x + y
print(add_two_numbers(2,5))
You need to actually run the inner function in the decorator if you want to use its output in the result of the decorator.
I didn't edit your first decorator, as it does what you want, but it actually isn't useful as a decorator. Since its output is not related in any way to the function it's decorating, it's just replacing the function. If you wanted to replace the function with that class, the way to do it would be
class square_result(object):
def __call__(self, x, y):
return (x+y)**2
# this has no effect at all after the reassignment
def add_two_numbers(x, y):
return x + y
add_two_numbers = square_result()
PEP8 also suggests CamelCase for your class names (SquareResult).
square_result doesn't "decorate" the add_two_numbers result, but overrides it (doing the addition as well as the squaring). It should instead treat the decorated function the same way that append_abc does, by storing it and then making use of the decorated function in its own implementation. Thus:
class square_result(object):
def __init__(self, f):
self.function = f
def __call__(self, *args):
return self.function(*args)**2
class append_abc(object):
def __init__(self, f):
self.function = f
def __call__(self, *args):
return str(self.function(*args)) + "abc"
#append_abc
#square_result
def add_two_numbers(x, y):
return x + y
print(add_two_numbers(2,5))

Categories

Resources