Pythonic在扩展kwargs时广播/循环遍历值的方法

时间:2017-01-28 03:16:45

标签: python python-3.x

我有一个函数foo,它有很多关键字参数:

def foo(blah=1, blih='abc', blohp=('improbable', 'towel', 42)):
    pass

我有一个函数bar,它在循环中调用函数foo,并展开kwargs

def bar(n, **kwargs):
    for i in range(n):
        foo(**kwargs)

我有三个同样重要的用例:

  1. 广播:kwargs是标准dictbar会针对foo的每次调用展开它(foo始终使用相同的关键字参数调用) 。这就是上面实现的内容。
  2. 循环:kwargs中的所有值都包含在某种迭代中(长度为n),而bar会扩展每个新调用foo中的每个新值1}}(每次使用不同的关键字参数调用foo)。
  3. 组合:kwargs中的某些值包含在某种迭代中,与2.类似,而有些则不是,与1.类似的广播。
  4. 请注意foo收到的实际关键字参数是任意的(因此它们实际上可能是一个可迭代的对象 - 如blihblohp)。

    实现这种行为并同时满足所有用例的简洁,pythonic模式是什么?

3 个答案:

答案 0 :(得分:1)

不确定这是否正是您所要求的:

from itertools import product, combinations
from operator import itemgetter


def wrap_as_iterable(val):
    """Make sure a value is always a list or tuple."""
    if isinstance(val, (tuple, list)):
        return val
    else:
        return (val, )


def foo(**kwargs):
    print(kwargs)  # Just printing the arguments


def bar(**kwargs):
    # Wrap each keyword argument as iterable
    for key, value in kwargs.items():
        kwargs[key] = wrap_as_iterable(value)

    # Loop through all combinations of the keyword-value combinations, that's
    # just the broadcasting case, no repeats. However repeats would be easy to
    # implement with the "range" you already have.
    for valuecomb in product(*kwargs.values()):
        foo(**dict(zip(kwargs, valuecomb)))

只是为了展示一些示例案例:

>>> bar(blah=1, blih='abc', blohp=('improbable', 'towel', 42))
{'blih': 'abc', 'blah': 1, 'blohp': 'improbable'}
{'blih': 'abc', 'blah': 1, 'blohp': 'towel'}
{'blih': 'abc', 'blah': 1, 'blohp': 42}

>>> bar(blah=(1,2,3), blih=('abc', 'def'), blohp=('improbable', 'towel', 42))
{'blih': 'abc', 'blah': 1, 'blohp': 'improbable'}
{'blih': 'abc', 'blah': 1, 'blohp': 'towel'}
{'blih': 'abc', 'blah': 1, 'blohp': 42}
{'blih': 'abc', 'blah': 2, 'blohp': 'improbable'}
{'blih': 'abc', 'blah': 2, 'blohp': 'towel'}
{'blih': 'abc', 'blah': 2, 'blohp': 42}
{'blih': 'abc', 'blah': 3, 'blohp': 'improbable'}
{'blih': 'abc', 'blah': 3, 'blohp': 'towel'}
{'blih': 'abc', 'blah': 3, 'blohp': 42}
{'blih': 'def', 'blah': 1, 'blohp': 'improbable'}
{'blih': 'def', 'blah': 1, 'blohp': 'towel'}
{'blih': 'def', 'blah': 1, 'blohp': 42}
{'blih': 'def', 'blah': 2, 'blohp': 'improbable'}
{'blih': 'def', 'blah': 2, 'blohp': 'towel'}
{'blih': 'def', 'blah': 2, 'blohp': 42}
{'blih': 'def', 'blah': 3, 'blohp': 'improbable'}
{'blih': 'def', 'blah': 3, 'blohp': 'towel'}
{'blih': 'def', 'blah': 3, 'blohp': 42}

答案 1 :(得分:1)

这是一种方法,假设您希望在bar和所有n==2都是可迭代时调用kwargs两次:

from collections import abc
from itertools import repeat

def foo(blah=1, blih='abc', blohp=('improbable', 'towel', 42)):
    print(blah, blih, blohp)

def bar(n, **kwargs):
    args = []
    for v in kwargs.values():
        # Turn single argument to iterable, treat strings as single arg
        if isinstance(v, str) or not isinstance(v, abc.Iterable):
            v = repeat(v)

        args.append(v)

    # Iterable that returns a tuple containing one item from each of the
    # iterables created above
    args = zip(*args)
    for i in range(n):
        foo(**dict(zip(kwargs, next(args))))

d = {
    'blah': 1,
    'blih': ['abc', 'def'],
    'blohp': [
        ('improbable', 'towel', 42),
        ('improbable', 'towel', 43)
    ]
}

bar(2, **d)
bar(2, blah='blah', blih='blih', blohp='blohp')

输出:

1 abc ('improbable', 'towel', 42)
1 def ('improbable', 'towel', 43)
blah blih blohp
blah blih blohp

答案 2 :(得分:1)

设置bar,以便您可以告诉它您要广播的内容:

def bar(iter_kwargs, broadcast_kwargs):
    for iter_kwarg in iter_kwargs:
        foo(**dict(broadcast_kwargs, **iter_kwarg))