获取实际传递给Python方法的关键字参数

获取实际传递给Python方法的关键字参数,python,arguments,keyword,Python,Arguments,Keyword,我梦想有一个带有显式关键字args的Python方法: def func(a=None, b=None, c=None): for arg, val in magic_arg_dict.items(): # Where do I get the magic? print '%s: %s' % (arg, val) 我只想得到调用方实际传递到方法中的那些参数的字典,就像**kwargs,但我不希望调用方能够传递任何旧的随机参数,这与**kwargs不同 >>

我梦想有一个带有显式关键字args的Python方法:

def func(a=None, b=None, c=None):
    for arg, val in magic_arg_dict.items():   # Where do I get the magic?
        print '%s: %s' % (arg, val)
我只想得到调用方实际传递到方法中的那些参数的字典,就像
**kwargs
,但我不希望调用方能够传递任何旧的随机参数,这与
**kwargs
不同

>>> func(b=2)
b: 2
>>> func(a=3, c=5)
a: 3
c: 5
那么:有这样的咒语吗?在我的例子中,我碰巧能够将每个参数与其默认值进行比较,以找到不同的参数,但这有点不雅观,当你有九个参数时,会变得单调乏味。为了获得额外的积分,请提供一个咒语,即使调用方传入指定了默认值的关键字参数,也可以告诉我:

>>> func(a=None)
a: None
狡猾

编辑:函数(词法)签名必须保持完整。它是公共API的一部分,显式关键字args的主要价值在于它们的文档价值。只是为了让事情变得有趣。:)

一种可能性:

def f(**kw):
  acceptable_names = set('a', 'b', 'c')
  if not (set(kw) <= acceptable_names):
    raise WhateverYouWantException(whatever)
  ...proceed...
_sentinel = object():
def f(a=_sentinel, b=_sentinel, c=_sentinel):
   ...proceed with checks `is _sentinel`...
通过创建唯一的对象
\u sentinel
可以消除调用方可能意外传递
None
(或调用方可能传递的其他非唯一默认值)的风险。这就是所有
object()
的优点,顺便说一句:一个非常轻量级的独特哨兵,不可能意外地与任何其他对象混淆(当您使用
is
操作符检查时)


对于稍有不同的问题,两种解决方案都是可取的。

使用装饰器来验证传入的KWARG如何

def validate_kwargs(*keys):
    def entangle(f):
        def inner(*args, **kwargs):
            for key in kwargs:
                if not key in keys:
                    raise ValueError("Received bad kwarg: '%s', expected: %s" % (key, keys))
            return f(*args, **kwargs)
        return inner
    return entangle

###

@validate_kwargs('a', 'b', 'c')
def func(**kwargs):
   for arg,val in kwargs.items():
       print arg, "->", val

func(b=2)
print '----'
func(a=3, c=5)
print '----'
func(d='not gonna work')
给出此输出:

b -> 2
----
a -> 3
c -> 5
----
Traceback (most recent call last):
  File "kwargs.py", line 20, in <module>
    func(d='not gonna work')
  File "kwargs.py", line 6, in inner
    raise ValueError("Received bad kwarg: '%s', expected: %s" % (key, keys))
ValueError: Received bad kwarg: 'd', expected: ('a', 'b', 'c')
b->2
----
a->3
c->5
----
回溯(最近一次呼叫最后一次):
文件“kwargs.py”,第20行,在
func(d=‘不起作用’)
文件“kwargs.py”,第6行,在内部
raise VALUE ERROR(“收到的坏消息:“%s”,应为:%s”%(键,键))
ValueError:收到错误的kwarg:'d',应为:('a','b','c')

也许有更好的方法可以做到这一点,但我的观点如下:

def CompareArgs(argdict, **kwargs):
    if not set(argdict.keys()) <= set(kwargs.keys()):
        # not <= may seem weird, but comparing sets sometimes gives weird results.
        # set1 <= set2 means that all items in set 1 are present in set 2
        raise ValueError("invalid args")

def foo(**kwargs):
    # we declare foo's "standard" args to be a, b, c
    CompareArgs(kwargs, a=None, b=None, c=None)
    print "Inside foo"


if __name__ == "__main__":
    foo(a=1)
    foo(a=1, b=3)
    foo(a=1, b=3, c=5)
    foo(c=10)
    foo(bar=6)
def CompareArgs(argdict,**kwargs):

如果未设置(argdict.keys())如果它们传递任何*参数,可能会引发错误

def func(*args, **kwargs):
  if args:
    raise TypeError("no positional args allowed")
  arg1 = kwargs.pop("arg1", "default")
  if kwargs:
    raise TypeError("unknown args " + str(kwargs.keys()))
将其分解为一个变量名列表或一个要使用的通用解析函数是很简单的。要将它变成一个装饰器(python 3.1)也不难:


注意:我不确定这对已经包装好的函数或已经有
*args
**kwargs
的函数的效果如何。

以下是最简单的方法:

def func(a=None, b=None, c=None):
    args = locals().copy()
    print args

func(2, "egg")
这将给出输出:
{'a':2,'c':无,'b':'egg'}
。 之所以
args
应该是
locals
字典的副本,是因为字典是可变的,因此如果在此函数中创建任何局部变量,则
args
将包含所有局部变量及其值,而不仅仅是参数


更多关于内置的
局部变量
函数的文档。

我受到lost theory的decorator Goods的启发,在玩了一会儿之后,我想到了这个:

def actual_kwargs():
    """
    Decorator that provides the wrapped function with an attribute 'actual_kwargs'
    containing just those keyword arguments actually passed in to the function.
    """
    def decorator(function):
        def inner(*args, **kwargs):
            inner.actual_kwargs = kwargs
            return function(*args, **kwargs)
        return inner
    return decorator


if __name__ == "__main__":

    @actual_kwargs()
    def func(msg, a=None, b=False, c='', d=0):
        print msg
        for arg, val in sorted(func.actual_kwargs.iteritems()):
            print '  %s: %s' % (arg, val)

    func("I'm only passing a", a='a')
    func("Here's b and c", b=True, c='c')
    func("All defaults", a=None, b=False, c='', d=0)
    func("Nothin'")
    try:
        func("Invalid kwarg", e="bogon")
    except TypeError, err:
        print 'Invalid kwarg\n  %s' % err
它打印了这个:

I'm only passing a a: a Here's b and c b: True c: c All defaults a: None b: False c: d: 0 Nothin' Invalid kwarg func() got an unexpected keyword argument 'e' 我只是路过一个小时 a:a 这是b和c b:是的 c:c 所有默认值 a:没有 b:错 c: d:0 没什么 无效夸格 func()获得意外的关键字参数“e” 我对此很满意。一种更灵活的方法是将要使用的属性的名称传递给装饰器,而不是将其硬编码为“actual_kwargs”,但这是说明解决方案的最简单方法


嗯,Python很好吃。

魔法不是答案:

def funky(a=None, b=None, c=None):
    for name, value in [('a', a), ('b', b), ('c', c)]:
        print name, value

这是通过sentry对象的单个实例最容易实现的:

# Top of module, does not need to be exposed in __all__
missing = {}

# Function prototype
def myFunc(a = missing, b = missing, c = missing):
    if a is not missing:
        # User specified argument a
    if b is missing:
        # User did not specify argument b

这种方法的好处是,由于我们使用“is”操作符,调用方可以传递一个空dict作为参数值,我们仍然会发现他们并不想传递它。我们还可以通过这种方式避免讨厌的装饰程序,并保持代码更干净。

很好——很接近——但c不应该在其中。它没有告诉我你通过了哪些。嘿,很好。我也喜欢这种方法。非常感谢。既然missing并不是一个dict,为什么不使用“object()”?这是对象的唯一(即点)实例。不可用于任何用途(不含欺骗)。哦,模块末尾缺少del,所以如果你想安全的话,它不能被导出。你不能删除它;函数需要它在全局范围内,因为它们从那里引用它。如果你真的担心的话,你可以在它前面加一个下划线。对象或命令将起作用;除了{}比object()更简洁之外,我不知道这两个问题有什么严格的好处。这个问题有一个非常相似的标题,但我不完全确定它是否是重复的:@AndersonGreen。你提到的问题与这个无关。它询问当使用**符号传递给一个不接受所有关键字的方法时,如何过滤字典。这是一个很好的问题……你想说什么?a、 k.a.伙计,我不明白
def funky(a=None, b=None, c=None):
    for name, value in [('a', a), ('b', b), ('c', c)]:
        print name, value
# Top of module, does not need to be exposed in __all__
missing = {}

# Function prototype
def myFunc(a = missing, b = missing, c = missing):
    if a is not missing:
        # User specified argument a
    if b is missing:
        # User did not specify argument b