Search code examples
pythonfunctional-programmingoperator-overloadingabcpython-3.3

Implementing pointwise arithmetic with implicit type conversion


Suppose I have class Function, whose instances are callables that take one argument. I defined pointwise arithmetic for these classes in the straightforward way. Here's a simplified version of my code (I actually have more complex behavior in __init__ and __call__ but it's irrelevant for this question):

class Function:
  '''
  >>> f = Function(lambda x : x**2)
  >>> g = Function(lambda x : x + 4)
  >>> h = f/g
  >>> h(6)
  3.6
  '''
  def __init__(self, func):
    self.func = func
  def __call__(self, value):
    return self.func(value)
  def __truediv__(self, other):
    if isinstance(other, Function):
        return Function(lambda x:self(x)/other(x))
    else:
        return NotImplemented
  # ...

I'm stuck when I try to allow implicit type conversions. For example, I want to be able to write:

>>> f = Function(lambda x : x ** 2)
>>> g = f+1
>>> g(5)
26

In other words, whenever I see a numeric object v in an arithmetic expression next to a Function instance, I want to convert v to Function(lambda x : v).

In addition, I want to achieve similar behavior for some of my user-defined types (again, whenever I see them in the same binary arithmetic expression with a Function object).

While I can certainly code this logic with a brute force assortment of regular and reflected binary arithmetic operators, each checking isinstance(v, numbers.Number), and isinstance(v, MyUserDefinedType), I feel there might be a more elegant way.

Also, if there are any other improvements possible with my design, please let me know. (Function objects are created rarely, but called very often, so performance is of some interest.)

EDIT:

To address @Eric's comment, I should clarify that I have another user-defined class Functional:

class Functional:
  '''
  >>> c = [1, 2, 3]
  >>> f = Functional(lambda x : x + 1)
  >>> f(c)
  [2, 3, 4]
  >>> g = Functional(lambda x : x ** 2)
  >>> h = f + g
  >>> h(c)
  [3, 7, 13]
  '''
  def __init__(self, func):
    self.func = func
  @staticmethod
  def from_function(self, function):
    return Functional(function.func)
  def __call__(self, container):
    return type(container)(self.func(c) for c in container)
  def __add__(self, other):
    if isinstance(other, Functional):
      return Functional(lambda x : self.func(x) + other.func(x))
    else:
      return NotImplemented

When I see both a Function and a Functional instance in the same arithmetic expression, I want Function to be implicitly converted to Functional using Functional.from_function method.

So, implicit type conversion hierarchy goes like this:

  • Functional
  • Function
  • anything else

And I'd like to implicitly convert to the highest type in this hierarchy seen in a given arithmetic expression.


Solution

  • Something like this for all operators would work well:

    def __truediv__(self, other):
      if callable(other):
          return Function(lambda x:self(x)/other(x))
      else:
          return Function(lambda x:self(x)/other)