6

我希望能够调度函数的不同实现,不仅基于第一个参数的类型,而且基于任意谓词。目前我必须这样做:

def f(param):
    try:
        if param > 0:
            # do something
    except TypeError:
        pass
    try:
        if all(isinstance(item, str) for item in param):
            # do something else
    except TypeError:
        raise TypeError('Illegal input.')

以下是我希望能够做的事情的精神:

@generic
def f(param):
    raise TypeError('Illegal input.')  # default

@f.when(lambda param: param > 0)
def f_when_param_positive(param):
    # do something

@f.when(lambda param: all(isinstance(item, str) for item in param))
def f_when_param_iterable_of_strings(param):
    # do something else

它类似于 Python 3 的singledispatch,但singledispatch仅支持对类型进行分派,而不支持对任意谓词进行分派。

TL;DR:是否有一个库允许基于任意谓词(不仅是参数的类型)对函数进行基于谓词的调度?

4

4 回答 4

3

感谢回复者。在问了这个问题之后,似乎没有现有的模块可以做到这一点。所以我自己写了:) 它的灵感来自@Elazar 的建议。

请随时查看。它在 PyPI 上,您可以使用以下方法安装它:

pip install genericfuncs

它也托管在 GitHub 上,我计划继续开发和添加功能,同时尽量保持 API 简单。欢迎投稿。

于 2016-01-18T21:47:04.723 回答
2

这是从@Yam 调整的解决方案以适合您的语法并用作库。决定(这是一个常见的决定)是第一个谓词获胜:

class guarded:
    def __init__(self, default):
        self.funcs = []
        self.default = default

    def when(self, pred):
        def add(func):
            self.funcs.append( (pred, func) )
            return func
        return add

    def __call__(self, *args, **kwargs):
        for pred, func in self.funcs:
            try:  
                match = pred(*args, **kwargs)
            except Exception:
                match = False
            if match:
                return func(*args, **kwargs)
        return self.default(*args, **kwargs)

用户代码:

@guarded
def f(param):
    raise TypeError('Illegal input')

@f.when(lambda param: param > 0)
def f_when_param_positive(param):
    return 'param_positive'

@f.when(lambda param: all(isinstance(item, str) for item in param))
def f_when_param_iterable_of_strings(param):
    return 'param_iterable_of_strings'

尝试一下,我们得到类似的东西:

>>> print(f(123))
param_positive
>>> print(f(['a', 'b']))
param_iterable_of_strings
>>> print(f(-123))
Traceback (most recent call last):
...
TypeError: Illegal input
于 2015-12-23T11:20:13.013 回答
1

我不知道图书馆,但这是一个基本的实现框架。在我看来,阻止这成为实际解决方案的真正问题是,我不知道如何在这里进行专门的解决方案1。那样的话,很可能会带来很多维护上的困难。

#!/usr/bin/python3  

class when(object):
  funcs = {}

  def __init__(self, pred):
    self.pred = pred

  def __call__(self, func):
    if func.__qualname__ not in when.funcs:
        when.funcs[func.__qualname__] = {}

    when.funcs[func.__qualname__][self.pred] = func

    return lambda *args, **kwargs: when.__match(func, *args, **kwargs)

  @staticmethod
  def __match(f, *args, **kwargs):
    for pred, func in when.funcs[f.__qualname__].items():
      if pred(*args, **kwargs):
          return func(*args, **kwargs)
    raise NotImplementedError()


@when(lambda x: x < 0)
def my_func(x):
  return "smaller!"

@when(lambda x: x > 0)
def my_func(x):
  return "greater!"


print(my_func(-123))
print(my_func(123))

[1]:分辨率的问题是不容易做对。这里有一些可供考虑的替代方案,所有这些都严重缺乏实施和使用的充分理由。

  1. 适用的专业化谓词可能很复杂,并且可能最好由用户掌握,以便为每个谓词手动定义等级/权重。这是笨拙的,通常是维护/样板头痛,不值得这种机制的最初魅力。
  2. 用户总是可以在程序运行时添加更多的重载(Python 被解释),这可能会导致令人惊讶的时间行为。在你的代码库中传播这个是自满的。当它不被传播时,为什么不只是if/else和被完成?
  3. 您可以以某种方式限制使用,并强制执行它,以便给定调用只有一个谓词必须返回 True。这在很多方面都是奇怪的、低效的和无用的,例如,如果您想捕获 A 或其子类的所有实例,但以特殊方式处理子类 C 怎么办?或者,如果您想进一步专门化具有额外条件的谓词。你打算如何对这种模型进行分类?
于 2015-12-23T08:24:06.310 回答
-1

您可以使用isintance它并将其与ABC组合来检查输入的特征,如下所示:

from collections.abc import Iterable

def foo(param):
    if isinstance(param,int) and param > 0:
        #do something 
    elif isinstance(param,Iterable) and all(isinstance(item, str) for item in param):
        # do something else
    else:
        raise TypeError('Illegal input.')

ABC 告诉您参数具有哪种接口,因此如果您不关心它是否是特定类型,您可以根据自己的操作使用适当的接口,因此定义为参数可能是 a setlisttuple字符串,并且总是会通过第二次检查,因此您可以相应地处理它。在这种情况下,还有数字的ABC也是你想要的一般性。

于 2015-12-22T23:06:55.500 回答