Python 检查参数是按位置传递还是通过关键字传递

Python 检查参数是按位置传递还是通过关键字传递,python,python-3.x,Python,Python 3.x,考虑一个签名为f(a,b)的函数。将来,我想将签名更改为f(a,*,b),不允许将b作为位置参数传递。 为了减少更改的影响,我想首先反对按位置指定b,并警告这样做的用户 为此,我想写下如下内容: def f(a, b): frame = inspect.currentframe() if b in frame.specified_as_positional: print('Do not do that') else: print('Good

考虑一个签名为f(a,b)的函数。将来,我想将签名更改为
f(a,*,b)
,不允许将b作为位置参数传递。 为了减少更改的影响,我想首先反对按位置指定b,并警告这样做的用户

为此,我想写下如下内容:

def f(a, b):
    frame = inspect.currentframe()
    if b in frame.specified_as_positional:
        print('Do not do that')
    else:
        print('Good')
结果是

>>> f(1, 2)
'Do not do that'
>>> f(1, b=2)
'Good'
inspect.getargvalues(frame)
似乎不够。ArgInfo对象只提供

>>> f(1,b=2)
ArgInfo(args=['a', 'b'], varargs=None, keywords=None, locals={'a': 1, 'b': 2})
这样的检查在Python中是否可行?从概念上讲,解释器似乎不需要记住参数是按位置指定的还是按关键字指定的


Python 2支持很好,但不是严格要求的。

这里有一个非常黑客的解决方案:

def f(a, c=None, b=None):
    if (b == None):
        print("do not do that")
    else:
        print("good")
其中输入:
f(1,b=2)
prints
good
f(1,2)
prints
不要这样做
您可以使用包装器在用户和函数之间添加额外的步骤。在该步骤中,您可以在名称起作用之前检查参数。请注意,这取决于
b
没有默认值,并且必须始终作为arg提供

functools.wrapps
用于使修饰后的函数在许多方面与原始函数相似

import functools
import warnings

def deprecate_positional(fun):
    @functools.wraps(fun)
    def wrapper(*args, **kwargs):
        if 'b' not in kwargs:
            warnings.warn(
                'b will soon be keyword-only',
                DeprecationWarning,
                stacklevel=2
                )
        return fun(*args, **kwargs)
    return wrapper

@deprecate_positional
def f(a, b):
    return a + b

唯一想到的是
def(a,弃用的\u b=None,**kwargs):
-如果
弃用的\u b
不是None,请给出警告,否则请查找
kwargs['b']
。不建议这样做,但解析dis输出可能是一个选项,例如,{x.opname中的
'CALL u FUNCTION\u KW in{x.opname for x.get\u指令('f(1,b=2)})
@jasonharper这是一个很好的建议。如果你把它作为答案发布,我很乐意接受。关于
==None
:另外,如果b是None,使用
而不是
,如果c不是None
,不是更好吗?按照您的方式,您将无法区分是否有人故意使用
b=None
@finefoot调用函数。我正在检查b,试图迫使用户为b传递一个值(而不是只传递一个参数)。我不认为传递的参数的值不能是None,但您是对的,它可以,这将导致我的实现中出现不需要的行为。(感谢您的链接!)您可能希望编辑您的答案以解决上述问题,因此我将删除我的评论。其他偶然发现这条线索的人将更容易通过这种方式。请参阅jasonharper对上述问题的评论以获取灵感。
>>> f(1, b=2)
3
>>> f(1, 2)
Warning (from warnings module):
  File "C:/Users/nwerth/Desktop/deprecate_positional.py", line 36
    print(f(1, 2))
DeprecationWarning: b will soon be keyword-only
3