如何强制某些函数参数仅是位置参数

我想在Python3.7中模仿Python3.8的这种行为


仅位置参数是引入的语法,用于指示某些函数参数必须按位置指定,并且不能用作关键字参数。/


#Python3.8

def f(a,b,/,**kwargs):

    print(a,b,kwargs)


>>> f(1,2,**{'a':100,'b':200,'c':300})

# 1 2 {'a': 100, 'b': 200, 'c': 300}

a,仅用作位置参数。b


我如何在Python3.7中做同样的事情


#Python3.7


def f(a,b,**kwargs):

    print(a,b,kwargs)


>>> f(1,2,**{'a':1,'b':2})

# TypeError: f() got multiple values for argument 'a'

如何使 , 仅作为位置参数。 在 Python3.8 的下面不起作用ab/


是否可以在 Python3.7 中模仿语法?/


莫回无
浏览 175回答 1
1回答

慕的地6264312

您可以创建一个自定义修饰器来声明仅位置参数,并返回一个包装器,该包装器解析自己的包装器,以便它们适合修饰函数的签名。由于仅位置参数和关键字参数之间可能存在名称冲突,因此无法对此方法使用关键字参数打包 ()(这是唯一的限制)。打包的关键字参数需要声明为最后一个位置或关键字参数,或者声明为第一个仅关键字参数。下面是两个示例:*args, **kwargs**def foo(a, b, kwargs):  # last positional-or-keyword parameter    passdef foo(a, *args, kwargs):  # first keyword-only parameter    pass该变量将从包装器函数接收剩余的,即它可以类似地使用,就像直接在修饰函数中使用一样(如在Python 3.8 +中)。kwargs**kwargs**kwargs以下装饰器的实现很大程度上是基于检查的实现。Signature.bind 通过一些小的调整来处理通过装饰器声明的名称仅位置参数,并处理附加(人为)参数。kwargsimport functoolsimport inspectimport itertoolsdef positional_only(*names, kwargs_name='kwargs'):    def decorator(func):        signature = inspect.signature(func)        @functools.wraps(func)        def wrapper(*args, **kwargs):            new_args = []            new_kwargs = {}                        parameters = iter(signature.parameters.values())            parameters_ex = ()            arg_vals = iter(args)            while True:                try:                    arg_val = next(arg_vals)                except StopIteration:                    try:                        param = next(parameters)                    except StopIteration:                        break                    else:                        if param.name == kwargs_name or param.kind == inspect.Parameter.VAR_POSITIONAL:                            break                        elif param.name in kwargs:                            if param.name in names:                                msg = '{arg!r} parameter is positional only, but was passed as a keyword'                                msg = msg.format(arg=param.name)                                raise TypeError(msg) from None                            parameters_ex = (param,)                            break                        elif param.default is not inspect.Parameter.empty:                            parameters_ex = (param,)                            break                        else:                            msg = 'missing a required argument: {arg!r}'                            msg = msg.format(arg=param.name)                            raise TypeError(msg) from None                else:                    try:                        param = next(parameters)                    except StopIteration:                        raise TypeError('too many positional arguments') from None                    else:                        if param.name == kwargs_name or param.kind == inspect.Parameter.KEYWORD_ONLY:                            raise TypeError('too many positional arguments') from None                        if param.kind == inspect.Parameter.VAR_POSITIONAL:                            new_args.append(arg_val)                            new_args.extend(arg_vals)                            break                        if param.name in kwargs and param.name not in names:                            raise TypeError(                                'multiple values for argument {arg!r}'.format(                                    arg=param.name)) from None                        new_args.append(arg_val)            for param in itertools.chain(parameters_ex, parameters):                if param.name == kwargs_name or param.kind == inspect.Parameter.VAR_POSITIONAL:                    continue                try:                    arg_val = kwargs.pop(param.name)                except KeyError:                    if (param.kind != inspect.Parameter.VAR_POSITIONAL                            and param.default is inspect.Parameter.empty):                        raise TypeError(                            'missing a required argument: {arg!r}'.format(                                arg=param.name)) from None                else:                    if param.name in names:                        raise TypeError(                            '{arg!r} parameter is positional only, '                            'but was passed as a keyword'.format(arg=param.name))                    new_kwargs[param.name] = arg_val            new_kwargs.update(kwargs=kwargs)            return func(*new_args, **new_kwargs)        return wrapper    return decorator以下是如何使用它的示例:@positional_only('a')def foo(a, *args, kwargs, b=9, c):    print(a, args, b, c, kwargs)foo(1, **dict(a=2), c=3)  # okfoo(1, 2, 3, 4, 5, c=6)  # okfoo(1, b=2, **dict(a=3), c=4)  # okfoo(a=1, c=2)  # errorfoo(c=1)  # error
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python