简体   繁体   中英

How to create function extensions / function interfaces / classes of functions in Python or functional programming languages?

Would like to define something I'd best call 'function extension' / 'function interface' or 'class of functions' in Python. Haven't seen similar constructs in other languages, but I'm no expert in functional languages like LISP. Considering to change to other language later if this is easier to do, say in Julia.

'Function extension' would be a formal way to express that binding few arguments of a function, we end up with a function belonging to a specific 'class of functions'. In my examples below the specific 'class of functions' is a data transformation function (as transform_data_func_class ), which has a sole argument: data . power_data extends transform_data_func_class as binding the exponent argument we end up with a function which belongs to the the 'class of functions' transform_data_func_class .

A 'class of functions' defines part of the signature like:

def transform_data_func_class(data):
    raise InterfaceError('Called unimplemented function class.')

Then would define functions extending the 'class of functions':

def power_data(data, exponent):  # extends: transform_data_func_class
    return data ** exponent      # apply the additional the parameter

So that when binding the additional parameter(s) we end up with a signature matching that of the extended 'class of functions':

square = partial(power_data, exponent=2)  # matches signature transform_data_func_class(data)

I'll use these for example, in order to specify types of functions as inputs and outputs of other functions:

def chain(data, transform_data_func_class_list):  
    """Passing in data and a list of functions being elements of 
    transform_data_func_class with a sole argument: data."""
    ...

Find it odd that while in the case of general objects it is relatively easy to define classes/types, in case of functions there is no such obvious construct. Currently I use extends: function_class comments to specify function types/classes - an idea borrowed from mypy - ideally would use something more formal.

Any suggestions welcome, would be great if I could just call the resulting construct through __call__ . Have thought of a class like:

class TransformDataFuncClass(object):
    @classmethod
    def exec(cls, data):
        raise InterfaceError('Called unimplemented function class.')

But don't like the ununtiutive exec() call. Have also thought of:

class TransformDataFuncClass(object):
    @classmethod
    def __call__(cls, data):
        raise InterfaceError('Called unimplemented function class.')

but I get TypeError: object() takes no parameters when calling TransformDataFuncClass(data=1) . Signaling 'function extension' through class inheritence would be a great way though.

My most elaborate plan was to do:

class FuncClass(object):
    """Takes care of function classes and extensions being 
    just callable through __call__, e.g. PowerData(data=1, power=2)
    instead of instantiating first: PowerData()(data=1, power=2)"""
    def __init__(self, wrapped_class):
       self.wrapped_class = wrapped_class
       self.wrapped_instance = wrapped_class()
    def __call__(self, *args, **kwargs):
       return self.wrapped_instance(*args, **kwargs)

@FuncClass
class TransformDataFuncClass(object):
    def __call__(self, data):
        raise InterfaceError('Called unimplemented function class.')

@FuncClass
class PowerData(TransformDataFuncClass.wrapped_class):
    def __call__(self, data, exponent):
        return data ** exponent      # apply the additional the parameter

So that TransformDataFuncClass(data=1) and PowerData(data=1, exponent=2) are natural and valid calls. While the latter actually worked its a bit complicated, and couldn't parallelize calculations as I got errors from dill (an smarter alternative to pickle ) saying that <class PowerData>: it's not the same object as PowerData .

Any suggestions welcome, really. I'm also interested in theoretical considerations and how this is done in other languages.

It's a little hard to see what you're trying to accomplish. Specifically, for these two function definitions:

def transform_data_interface(data):
    raise InterfaceError('Called unimplemented interface.')

def power_data(data, param1):  # extends: transform_data_interface
    return data ** param1      # apply the additional the parameter

How does power_data extend transform_data_interface ? They don't have the same signature: the "interface" takes a single argument, while the function implementing the interface takes two. That doesn't match what most people would say is an interface .

If what you're trying to do is enforce that a given function has the same arguments and return value as another function, you can accomplish this by inspecting the function's arguments.

  • the inspect module in the stdlib can tell you what arguments a function takes, specifically, Signatures give a nice API for this
  • function annotations allow you to mark up the parameters and return values of a function, this could replace what you're trying to do with the "# extends:" comment
  • also see the new proposal on using function annotations for optional typing and mypy
  • a decorator is the perfect place to add this kind of checking, as it wraps the function before it executes, lots of people have made type checking decorators (examples: 1 , 2 , 3 )

For your chain function, see Composing functions in python . You can modify one of those to also do the kind of checking I mentioned above.

As far as how other more functional languages do this kind of thing, I'm not sure. I see type classes mentioned in that interface article I linked to above, looks like there's some good info there.

If you have more specifics or background on what you're trying to do please update your post!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM