【问题标题】:How to create function extensions / function interfaces / classes of functions in Python or functional programming languages?如何在 Python 或函数式编程语言中创建函数扩展/函数接口/函数类?
【发布时间】:2015-04-16 04:57:22
【问题描述】:

想在 Python 中定义一些我最好称之为“函数扩展”/“函数接口”或“函数类”的东西。还没有在其他语言中看到类似的结构,但我不是 LISP 等函数式语言的专家。如果这更容易做到,考虑稍后更改为其他语言,比如在 Julia 中。

“函数扩展”将是一种正式的方式来表达绑定一个函数的几个参数,我们最终得到一个属于特定“函数类”的函数。在我下面的示例中,特定的“函数类”是一个数据转换函数(如transform_data_func_class),它有一个唯一的参数:data。 power_data 扩展 transform_data_func_class 作为绑定 exponent 参数,我们最终得到一个属于“函数类”transform_data_func_class 的函数。

“函数类”定义了部分签名,例如:

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

然后将定义扩展“函数类”的函数:

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

因此,当绑定附加参数时,我们最终会得到一个与扩展的“函数类”匹配的签名:

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

例如,我将使用这些来指定函数类型作为其他函数的输入和输出:

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."""
    ...

奇怪的是,虽然在一般对象的情况下定义类/类型相对容易,但在函数的情况下却没有这样明显的构造。目前我使用extends: function_class cmets 来指定函数类型/类——这是从mypy 借来的一个想法——理想情况下会使用更正式的东西。

欢迎任何建议,如果我可以通过__call__ 调用生成的构造,那就太好了。想过这样的课程:

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

但不喜欢不合理的 exec() 调用。也想过:

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

但我在拨打TransformDataFuncClass(data=1) 时收到TypeError: object() takes no parameters。不过,通过类继承发出“功能扩展”信号将是一种好方法。

我最详尽的计划是:

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

所以TransformDataFuncClass(data=1) 和PowerData(data=1, exponent=2) 是自然而有效的调用。虽然后者实际上工作起来有点复杂,并且无法并行计算,因为我从 dill(pickle 的一个更聪明的替代品)那里得到错误,说 <class PowerData>: it's not the same object as PowerData。

欢迎任何建议,真的。我也对理论上的考虑以及在其他语言中如何做到这一点感兴趣。

【问题讨论】:

  • 这与函数重载有何不同?
  • 我需要几个具有相同(或扩展)签名但名称不同的函数。 “函数重载或方法重载是创建具有不同实现的多个同名方法的能力”。假设MultiplyData(data, param1) 与PowerData(data, param1) 具有相同的签名。我正在寻找的更像是对象的接口/抽象类。一旦你修复了一个类的移动部分,它就会匹配继承的接口。
  • 当然有点超出我的预期,我的第一直觉是建议一个元类,但我确信我没有完全掌握这个概念 - 虽然这些可能是一个开始......A quest for pythonic interfaces和A Primer on Python Metaclasses

标签: python types lambda functional-programming lambda-calculus


【解决方案1】:

很难看出你想要完成什么。具体来说,对于这两个函数定义:

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

power_data 如何扩展transform_data_interface?它们没有相同的签名:“接口”接受一个参数,而实现接口的函数接受两个。这与大多数人所说的 interface 不符。

如果您想要强制一个给定函数具有与另一个函数相同的参数和返回值,您可以通过检查函数的参数来完成此操作。

有关您的chain 功能,请参阅Composing functions in python。您可以修改其中一个来进行我上面提到的那种检查。

至于其他功能性更强的语言如何做这种事情,我不确定。我看到我在上面链接的那篇界面文章中提到了type classes,看起来那里有一些很好的信息。

如果您对您正在尝试做的事情有更多细节或背景,请更新您的帖子!

【讨论】:

  • 感谢您的建议,改进了我的整体解释,并添加了第二段,希望能解释得更清楚。 Mypy 是一个很棒的项目,而我在 Python 2.7 上迫不及待升级到 3!
  • @MarkHorvath,lost-theory 的回答让我找到了 Python Cookbook,它可能会给你一些想法:Enforcing an Argument Signature on *args and **kwargs 和 Defining an Interface or Abstract Base Class
  • @wwii,我自己可能应该使用 ABC。检查可能是严格允许某个签名的函数作为参数的方法。尽管如此,我需要更多的是一种在代码中表达签名类型系统的方法......会尝试更多,也许这只是我不了解dill序列化的一件小事。
猜你喜欢
  • 2010-10-25
  • 2017-01-28
  • 2021-08-07
  • 2015-04-30
  • 1970-01-01
  • 1970-01-01
  • 2011-04-26
  • 2021-12-04
  • 1970-01-01
相关资源
最近更新 更多