利用测井装置优化从功能中提取kwargs

时间:2018-03-13 16:14:21

标签: python python-3.x logging python-decorators kwargs

最好的方法是不必提取SAME KWARGS 两次:一次在装饰器(包装函数)中,一次在函数本身中。

这是功能:

@close_logger
def close(**kwargs):
    """ returns close object"""

    # Get params
    session_attributes = kwargs.get('sessionAttrbiutes', {})
    message = kwargs.get('message', '')
    LID = kwargs.get('LIData', {})
    SANS = kwargs.get('SANS', [])
    FS = kwargs.get('fulfillmentState', 'Fulfilled')

    response = {
        'sessionAttributes': session_attributes,
        'dialogAction': {
            'type': SANS,
            'fulfillmentState': FS,
            'message': {
                'contentType': LID,
                'content': message
            }
        }
    }

    return response

这里是装饰器(用于记录close事件):

def close_logger(func):

    @functools.wraps(func)
    def wrapper(**kwargs):

        # Get params
        session_attributes = kwargs.get('sessionAttrbiutes', {})
        message = kwargs.get('message', '')
        LID = kwargs.get('LIData', {})
        SANS = kwargs.get('SANS', [])
        FS = kwargs.get('fulfillmentState', 'Fulfilled')

        logger.debug('Logging:\n Function:{} Session Attributes: {}\n \
        Message{}\n: LID: {}\n SANS: {}\n FS: {}'.format(
            func.__name__,
            session_attributes,
            message,
            LID,
            SANS,
            FS
        ))

        return func(**kwargs)

    return wrapper

2 个答案:

答案 0 :(得分:0)

首先明确close函数的关键字参数。

def close(sessionAttrbiutes=None, message='', LIData=None, SANS=None, fulfillmentState='Fulfilled'):
    if sessionAttrbiutes is None:
        sessionAttrbiutes = {}
    ...

请注意,我使用None作为可变默认值的默认值,以避免使用common pitfall

然后在装饰者中使用inspect.getfullargspec,类似于this answer

import inspect
import functools

def get_default_args(func):
    """
    returns a dictionary of arg_name:default_values for the input function
    """
    argspec = inspect.getfullargspec(func)
    return dict(zip(reversed(argspec.args), reversed(argspec.defaults)))


def close_logger(func):
    @functools.wraps(func)
    def wrapper(**kwargs):
        kwargs_local = get_default_args(func)
        kwargs_local.update(kwargs)

        logger.debug("""Logging:
Function: {}
Session Attributes: {sessionAttrbiutes}
Message: {message}
LID: {LIData}
SANS: {SANS}
FS: {fulfillmentState}""".format(func.__name__, **kwargs_local))

        return func(**kwargs)

    return wrapper

如果未定义其中一个字段(在签名或传递的关键字中),则会引发KeyError。当然,对于那些签名中的默认值,它会显示None

完整的argspec还将包含位置参数,因此您也可以将其解决。

答案 1 :(得分:0)

** - 可以在函数参数列表中使用表示法来打包和解包参数。

** - 函数定义中的符号将所有值收集到字典中。

>>> def kwargs_in_definition(**kwargs): return kwargs
>>> kwargs_in_definition(arg1 = 1, arg2 = 2, arg3 = 3)
{'arg1': 1, 'arg2': 2, 'arg3': 3}

** - 函数调用中的符号将所有值解包为关键字argumnts。

def kwargs_in_call(arg1 =0, arg2 = 0, arg3 = 0): return arg1, arg2, arg3

所以当你传递kwargs(kwargs:= {' arg1':1,' arg2':2,' arg3':3})到{{ 1}}你得到了这个:

kwargs_in_call

...但是如果你先解压缩它:

>>> kwargs_in_call(kwargs)
({'arg1': 1, 'arg2': 2, 'arg3': 3}, 0, 0) # kwargs is used as arg1.

所有这些都适用于>>> kwargs_in_call(**kwargs) (1, 2, 3) # kwargs unapcks to "arg1 = 1, arg2 = 2, arg3 = 3"

您的具体案例:

功能定义中的

*会为您提供常规的**kwargs。您可以将该字典转换为列表 - 但是您希望这样做 - 解压该列表并将其传递给另一个函数。但是,您必须考虑订购,因此很难将其用于所有功能。

我不确定为什么你坚持你的装饰器功能在你的装饰功能没有关键字参数的情况下。当然,你可以做任何你想做的事,但我会反对这种方法,因为你的装饰功能正在改变你的实际功能的行为。

如果您设法将这些关键字参数排序到有效的参数列表中,那么您将会很好。

希望这有帮助。

编辑:哦,我忘了指出你在装饰函数(dict)中解包**kwargs,然后你重新打包它们在你的实际功能中return func(**kwargs)。我说实话,这是一件很蠢的事。

编辑:您可以使用inspect.getargspecinspect.signature来获取函数参数列表。如果您只是让装饰者接受def close(**kwargs):,您可以将每个值与参数列表中的相应名称相匹配。