Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何强制某些函数参数仅为位置参数_Python_Function_Parameters_Python 3.7 - Fatal编程技术网

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

Python 如何强制某些函数参数仅为位置参数,python,function,parameters,python-3.7,Python,Function,Parameters,Python 3.7,我想在Python3.7中模拟Python3.8的这种行为 #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 def f(a,b,/,**kwargs): pri

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

#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
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'
如何使
a
b
成为唯一的位置参数<代码>/在下面的Python3.8中不起作用


可以在Python3.7中模拟
/
语法吗?

您可以创建一个自定义装饰器,声明仅位置参数,返回一个包装器,该包装器解析自己的
*args、**kwargs
,以使它们适合修饰函数的签名。由于仅位置参数和关键字参数之间可能存在名称冲突,因此无法将关键字参数打包(
***
)用于此方法(这是唯一的限制)。压缩关键字参数需要声明为最后一个位置参数或关键字参数,或者声明为第一个纯关键字参数。以下是两个例子:

def foo(a, b, kwargs):  # last positional-or-keyword parameter
    pass

def foo(a, *args, kwargs):  # first keyword-only parameter
    pass
变量
kwargs
将从包装函数接收剩余的
**kwargs
,也就是说,它可以类似地使用,就好像在修饰函数中直接使用了
**kwargs
(如Python 3.8+)

decorator的以下实现主要基于的实现,并进行了一些小的调整,以通过decorator声明的名称处理仅位置参数,并处理附加的(人工)
kwargs
参数

import functools
import inspect
import itertools


def 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)  # ok
foo(1, 2, 3, 4, 5, c=6)  # ok
foo(1, b=2, **dict(a=3), c=4)  # ok
foo(a=1, c=2)  # error
foo(c=1)  # error

似乎与此相关:@dspencer要将它们强制作为位置参数,唯一的解决方法是使用
*args
,但我只想将前两个参数作为位置参数。仍然在那里检查所有的答案。谢谢你抽出时间。@a_客人你为什么要删除答案。我还在测试它。@Ch3steR-Hm可能有一些额外的逻辑。我会考虑一下,并在我有东西的时候发布一个更新。@Ch3steR有可能您将函数定义为
foo(a,kwargs)
,也就是说,没有参数打包,并立即对其进行修饰。decorator将使用关键字参数并将它们作为一个简单的dict传递。没有decorator,函数的工作方式当然会非常不同。可以接受吗?