我有一个通用的平滑功能,并且基于一个配置文件(将作为字典加载的yaml),将调用不同的实现(boxcar或gaussian)。这些实现具有不同数量的参数,例如boxcar需要winsize,而高斯需要winsize和variance。
这是我目前的实施:
def smoothing(dataDf, selected_columns, kwargs):
method = kwargs['method']
if method == 'boxcar':
boxcar(dataDf, selected_columns, kwargs['arguments'])
elif method == 'gaussian':
gaussian(dataDf, selected_columns, kwargs['arguments'])
else:
raise NotImplementedError
有没有更好的方法来实现这个?
答案 0 :(得分:2)
我会考虑两个选项
使用函数字典:
methods = {
'boxcar': function1,
'gaussian': function2
}
try:
method = methods[kwargs['method']]
...
except KeyError:
raise NotImplementedError
你可以使它更加用户友好
def smoothing(dataDf, selected_columns, method, *args, **kwargs):
try:
return methods[kwargs['method']](
dataDf, selected_columns, *args, **kwargs
)
except KeyError:
raise NotImplementedError('{} is not a valid method'.format(method))
使用multiple dispatch。它允许您分发函数签名和类型
In [1]: from multipledispatch import dispatch
In [2]: @dispatch(int, int)
...: def f(x, y):
...: return x, y
...:
In [3]: @dispatch(int)
...: def f(x):
...: return x
...:
In [5]: f(1)
Out[5]: 1
In [6]: f(1, 2)
Out[6]: (1, 2)
在你的情况下
@dispatch(...list some/all argument types here...)
def smoothing(...signature for boxcar...):
pass
@dispatch(...list some/all argument types here...)
def smoothing(...signature for gaussian...)
pass
答案 1 :(得分:1)
我会说这个问题是基于主要意见的,但我有点......很无聊,所以这是我的看法:
在这些情况下,我总是倾向于优先考虑其他用户的可读性。我们都知道代码应该被正确评论,解释,周围有很多文档,对吗?但我也应该去健身房然而,在这里,我是从沙发的舒适性中写出来的(我不打算放弃,直到4月中旬,或多或少,当天气好转时。)
对我来说,如果其他人要阅读您的代码,我认为利用Python这一事实非常重要,如果编写得当,可以非常非常清楚(这几乎就像运行的伪代码一样,对吧?)
所以在你的情况下,我甚至不会创建这种包装函数。我会有一个包含所有平滑函数的模块smoothing.py
。不仅如此,我还会导入模块(import smoothing
)而不是from smoothing import boxcar, gaussian
),因此我可以非常明确调用我的电话:
if method == 'boxcar':
smoothing.boxcar(whatever whatever...)
# Someone reading this will be able to figure out that is an smoothing
# function from a module called `"smoothing"`. Also, no magic done with
# things like my_func = getattr(smoothing, method)... none of that: be
# clear and explicit.
# For instance, many IDEs allow you to navigate to the function's
# definition, but for that to work properly, the code needs to be explicit
elif method == 'gaussian':
smoothing.gaussian(whatever whatever...)
else:
raise ValueError(
'Unknown smoothing method "%s".'
' Please see available method in "%s" or add'
' a new smoothing entry into %s' % (
method,
os.path.abspath(smoothing.__file__),
os.path.abspath(__file__)
)
)
这样的事情。如果有人收到错误,可以快速了解它发生的地点和原因。
否则,如果你仍然希望保留你的结构,我会说,因为你总是需要你的'方法',所以不要把它放到你的kwargs中。使其成为位置:
def smoothing(method, dataDf, selected_columns, kwargs):
if method == 'boxcar':
boxcar(dataDf, selected_columns, kwargs['arguments'])
elif method == 'gaussian':
gaussian(dataDf, selected_columns, kwargs['arguments'])
else:
raise NotImplementedError
你可以做的另一件事是,而不是在kwargs
dict中有可能有坏参数,强制它包含正确的参数(如果有人传入kwarg['arguments']
参数{{ 1}}但是为method=boxcar
提供了kwarg['arguments']
?不要让它成为可能(让它尽快崩溃):
gaussian
并始终在您的例外中提供正确的讯息(对您的def smoothing(method, dataDf, selected_columns, **kwargs):
if method == 'boxcar':
assert 'variance' not in kwargs # If `boxcar` shouldn't have a "variance"
boxcar(dataDf, selected_columns, kwargs['windsize'])
elif method == 'gaussian':
gaussian(dataDf, selected_columns, kwargs['windsize'], kwargs['variance'])
else:
raise NotImplementedError
你可以用Python做很多的“魔法”。这并不意味着你必须这样做。例如,通过编写类似这样的内容,您可以获得与NotImplementedError
函数的实现相似的行为:
smoothing
但是如果有人读到那个......那么......祝福那个人: - P
答案 2 :(得分:1)
methods = {
'boxcar': boxcar,
'gaussian': gaussian,
}
MESSAGES = {
'MISSING_METHOD': 'No method named {} was found.'
}
def smooth(dataDf, selected_columns, **kwargs):
"""Smooth dataframe columns."""
# Here, we are providing a default if the
# user doesn't provide a keyword argument
# for `method`. You're accessing it with
# brackets and if it's not provided it will
# raise a KeyError. If it's mandatory, put
# it as such. But you can do better by
# providing a default. Like below
method_name = kwargs.get('method', 'gaussian')
method = methods.get(method_name)
if method is None:
msg = MESSAGES['MISSING_METHOD'].format(method_name)
raise NotImplementedError(msg)
return method(dataDf, selected_columns, **kwargs)
如果method
是调用的一个相当重要的部分,并且用户知道该参数,则可以按如下方式编写:
def smooth(dataDf, selected_columns, method='gaussian', **kwargs):
"""Smooth dataframe columns."""
# Note that kwargs no longer contains `method`;
# our call to the 'real' method is not 'polluted'.
_method = methods.get(method)
if _method is None:
msg = MESSAGES['MISSING_METHOD'].format(method)
raise NotImplementedError(msg)
return _method(dataDf, selected_columns, **kwargs)
但是,methods
字典中存在问题:
您希望获得一个字典,其中键和值是字符串(例如您从YAML文件中获取的字符串)。
我将做的假设是当前上下文中存在函数,无论您是定义它们还是导入它们。
from third.party.library import really_ugly_name_you_didnt_choose_but_imported
def gaussian(dataDf, selected_columns, **kwargs):
pass
_methods = globals()
# This is a dictionary where keys and values
# only contain strings, like from a YAML file.
methods = {
'boxcar': 'boxcar',
'gaussian': 'gaussian',
'roundcar': 'really_ugly_name_you_didnt_choose_but_imported',
}
MESSAGES = {
'MISSING_METHOD': 'No method named {} was found.'
}
def smooth(dataDf, selected_columns, method='gaussian', **kwargs):
"""Smooth dataframe columns."""
# Lets check if it is in authorized methods
# We're using globals() and we don't want
# the user to accidentally use a function
# that has no relation to smoothing.
# So we're looking at the dictionary from
# the YAML file.
# Let's get the "real name" of the function
# so if `method` were (str) 'roundcar', `method_name`
# would be (str) 'really_ugly_name_you_didnt_choose_but_imported'
method_name = methods.get(method)
# Now that we have the real name, let's look for
# the function object, in _methods.
_method = _methods.get(method_name)
if None in (_method, method_name):
msg = MESSAGES['MISSING_METHOD'].format(method)
# Note that we raise the exception for the function
# name the user required, i.e: roundcar, not
# the real function name the user might be unaware
# of, 'really_ugly_name_you_didnt_choose_but_imported'.
raise NotImplementedError(msg)
return _method(dataDf, selected_columns, **kwargs)
答案 3 :(得分:0)
您的算法函数为Strategies
。您可以将它们存储在字典中以便于查找。
使用defaultdict
“未实施”策略来删除算法:
def algorithm_not_implemented(*args, **kwargs):
raise NotImplementedError
algorithms = defaultdict(algorithm_not_implemented)
这意味着如果您尝试访问不存在的算法,它将返回algorithm_not_implemented
,当您调用它时,它将引发NotImplementedError
:
>>> algorithms['pete'](1, 2, 3)
Traceback (most recent call last):
NotImplementedError
您可以添加算法:
algorithms['boxcar'] = boxcar
algorithms['gaussian'] = gaussian
你可以打电话给他们:
def smoothing(dataDf, selected_columns, kwargs):
method = kwargs['method']
arguments = kwargs['arguments']
algorithms[method](dataDf, selected_columns, arguments)