我希望能够根据第一个参数的类型,而不是基于任意谓词来分派函数的不同实现。目前我必须这样做:
HKSample
这里有一些本着我能够做的事情的精神:
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.')
它类似于Python 3的@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
,但singledispatch
仅支持对类型的调度,而不支持任意谓词。
TL; DR:是否有一个库允许基于任意谓词(不仅是参数类型)的基于谓词的函数调度?
答案 0 :(得分:3)
感谢回复者。在提出这个问题之后,似乎没有现成的模块可以做到这一点。所以我写了自己的:)它的灵感来自@Elazar的建议。
请随时查看。 It's on PyPI您可以使用以下命令安装它:
pip install genericfuncs
它也是hosted on GitHub,我计划在尝试保持API简单的同时继续开发和添加功能。欢迎提供捐助。
答案 1 :(得分: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
答案 2 :(得分: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]:解决方案的问题在于,要做到正确并不容易。以下是一些需要考虑的替代方案,所有这些方法都缺乏实施和使用的充分理由。
if/else
并且完成?答案 3 :(得分:-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会告诉你这个参数有什么样的界面,所以如果你不关心它是不是特定类型,你可以根据自己的行为使用合适的界面,这样定义就像那个参数一样可能是set
,list
或tuple
字符串,并且始终会经过第二次检查,因此您可以相应地处理它。还有,numbers的ABC也是你想要在这种情况下也是一般的。