具有位置参数的函数的Decorator,具有通常命名的参数

时间:2014-01-22 10:03:21

标签: python python-2.7 decorator kwargs

我有一组函数/方法,它们都有不同的位置参数集 - 在某些情况下 - 也是关键字参数,但是共享一个名为 lane_type 的字符串参数。它可以是位置参数或关键字参数。 由于设计缺陷(有罪有罪),不同地方的相同值可能在字符串中包含大写字母。因此,为了进行比较,我必须将其转换为小写。 最终我决定尝试通过装饰器进行转换:

def lt_dec(func):
    def _lt_lower(**kwargs):
        kwargs['lane_type'] = kwargs['lane_type'].lower()
        return func(**kwargs)
    return _lt_lower

当然,我试图测试它 - 它失败了:

In [38]: @lt_dec
def test1(lane_type):
    print lane_type
   ....:     

In [39]: test1('Solid')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/homes/markg/<ipython-input-39-bb6cef5c7fad> in <module>()
----> 1 test1('Solid')

TypeError: _lt_lower() takes exactly 0 arguments (1 given)
显然,我并不完全了解** kwargs的替代机制。我将非常感谢我解释我所缺少的内容以及是否有一个优雅的解决方案来解决我的问题。

修改

我认为为了清楚起见,我应该展示一些功能定义的例子: 这是一个类

中的一种方法
def add_polygon(self, points, lane_type, color):

另一个类的方法:

def __init__(self, points, lane_type, color, side=None):

功能

def lane_weight(lane_type):

当然,对于带有1个参数的函数(我有几个),解决方案很简单

def lt_dec(func):
    def _lt_lower(lane_type):
        return func(lane_type)
    return _lt_lower

如上所述 - 对于所有示例,是否存在通用解决方案? (我准备接受否作为回答)

2 个答案:

答案 0 :(得分:4)

在函数定义中使用**kwargs,可以将关键字参数(不是位置参数)作为字典:

>>> def lt_dec(func):
...     def _lt_lower(**kwargs):
...         print 'type(kwargs)=', type(kwargs)
...         print 'kwargs=', kwargs
...         kwargs['lane_type'] = kwargs['lane_type'].lower()
...         return func(**kwargs)
...     return _lt_lower
...
>>> @lt_dec
... def test1(lane_type):
...     print lane_type
...
>>> test1(lane_type='Solid')
type(kwargs)= <type 'dict'>
kwargs= {'lane_type': 'Solid'}
solid

>>> def lt_dec(func):
...     def _lt_lower(*args, **kwargs):
...         if args:
...             args = (args[0].lower(),) + args[1:]
...         elif 'lane_type' in kwargs:
...             kwargs['lane_type'] = kwargs['lane_type'].lower()
...         return func(*args, **kwargs)
...     return _lt_lower
...
>>> @lt_dec
... def test1(lane_type):
...     print lane_type
...
>>> test1('Solid')
solid
>>> test1(lane_type='Solid')
solid

<强>更新

使用inspect.getcallargs

>>> import inspect
>>>
>>> def lt_dec(func):
...     def _lt_lower(*args, **kwargs):
...         kwargs = inspect.getcallargs(func, *args, **kwargs)
...         if 'lane_type' in kwargs:
...             kwargs['lane_type'] = kwargs['lane_type'].lower()
...         return func(**kwargs)
...     return _lt_lower
...
>>> @lt_dec
... def test1(blah, lane_type):
...     print lane_type
...
>>> test1('foo', 'Solid')
solid
>>> test1('foo', lane_type='Solid')
solid

答案 1 :(得分:0)

这是装饰器的版本,通过在装饰时使用它来减少 inspect 的影响 - 而不是在执行时。执行影响仍然相对较高 - 但比你提出的原始装饰器(只是FYI)低100倍

def lane_type_lower(func):

    def _conv_pos(*args, **kwargs):
        args = list(args);
        args[pos] = args[pos].lower()
        return args, kwargs

    def _conv_kw(*args, **kwargs):
        kwargs['lane_type'] = kwargs['lane_type'].lower()
        return args, kwargs

    insp = inspect.getargspec(func)
    try:
        pos = insp.args.index('lane_type')
        conv_func = _conv_pos
    except ValueError:
        conv_func = _conv_kw

    def _lane_type_lower(*args, **kwargs):
        args, kwargs = conv_func(*args, **kwargs)
        return func(*args, **kwargs)

    return _lane_type_lower