如果它们是None,则调用没有可选参数的函数

luc*_*rot 29 python python-3.x

有一个带有可选参数的函数.

def alpha(p1="foo", p2="bar"):
     print('{0},{1}'.format(p1, p2))
Run Code Online (Sandbox Code Playgroud)

让我迭代一下当我们以不同方式使用该函数时会发生什么:

>>> alpha()
foo,bar
>>> alpha("FOO")
FOO,bar
>>> alpha(p2="BAR")
foo,BAR
>>> alpha(p1="FOO", p2=None)
FOO,None
Run Code Online (Sandbox Code Playgroud)

现在考虑我想要调用它的情况,alpha("FOO", myp2)并且myp2将包含要传递的值,或者是None.但即使该函数处理p2=None,我希望它使用其默认值"bar".
也许这令人困惑,所以让我改写一下:

如果myp2 is None,打电话alpha("FOO").不用了,打电话alpha("FOO", myp2).

区别是相关的,因为alpha("FOO", None)结果不同alpha("FOO").

我如何简洁(但可读)做出这种区分?

一种可能性通常是在内部检查无alpha,这将被鼓励,因为这将使代码更安全.但是假设alpha它实际上应该在其他地方使用,None就像它一样.

我想在来电方面处理这个问题.

一种可能性是区分案例:

if myp2 is None:
    alpha("FOO")
else:
    alpha("FOO", myp2)
Run Code Online (Sandbox Code Playgroud)

但是,当有多个这样的论点时,这很快就会成为很多代码.(指数,2 ^ n)

另一种可能性是简单地做alpha("FOO", myp2 or "bar"),但这需要我们知道默认值.通常,我可能会采用这种方法,但我可能稍后更改默认值,alpha然后需要手动更新此调用,以便仍然使用(新)默认值调用它.

我使用Python 3.4,但是这将是最好的,如果你的答案能够提供在任何Python版本工作的好办法.


这个问题在技术上完成了,但是我再次重新考虑了一些要求,因为第一个答案确实掩盖了这一点:
我希望保留alpha其默认值的行为"foo", "bar",因此它(可能)不是改变alpha自身的选项.
换句话说,假设在alpha其他alpha("FOO", None)地方使用输出FOO,None是预期行为的地方.

dec*_*eze 31

将参数作为kwargs从字典中传递出来,从中过滤掉None值:

kwargs = dict(p1='FOO', p2=None)

alpha(**{k: v for k, v in kwargs.items() if v is not None})
Run Code Online (Sandbox Code Playgroud)

  • 它只是使用 [argument unpacking](https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists) 和 [dictionary comprehensions](https://docs.python.org/3/教程/datastructures.html#list-comprehensions)。 (3认同)
  • 这当然对待 `p1 = None` 是一样的,可能是想要的(或不相关的)但值得注意的是 (2认同)
  • 这工作得很好!谢谢!你介意添加一个简短的解释或一个好的链接吗?我想我了解它的作用,并且可以自己谷歌其余部分,但它对未来的访问者也很有用 (2认同)

luc*_*rot 9

虽然**绝对是一种语言功能,但它肯定不是为解决这个特殊问题而创建的.你的建议是有效的,我的也是如此.哪一个更好用取决于OP代码的其余部分.但是,仍然没有办法写f(x or dont_pass_it_at_all) - blue_note

感谢你的好答案,我想我会尽力做到这一点:

# gen.py
def callWithNonNoneArgs(f, *args, **kwargs):
    kwargsNotNone = {k: v for k, v in kwargs.items() if v is not None}
    return f(*args, **kwargsNotNone)
Run Code Online (Sandbox Code Playgroud)

 

# python interpreter
>>> import gen
>>> def alpha(p1="foo", p2="bar"):
...     print('{0},{1}'.format(p1,p2))
...
>>> gen.callWithNonNoneArgs(alpha, p1="FOO", p2=None)
FOO,bar
>>> def beta(ree, p1="foo", p2="bar"):
...     print('{0},{1},{2}'.format(ree,p1,p2))
...
>>> beta('hello', p2="world")
hello,foo,world
>>> beta('hello', p2=None)
hello,foo,None
>>> gen.callWithNonNoneArgs(beta, 'hello', p2=None)
hello,foo,bar
Run Code Online (Sandbox Code Playgroud)

这可能并不完美,但它似乎有效:它是一个函数,你可以调用另一个函数和它的参数,它应用deceze的答案来过滤掉那些参数None.

  • 与最高的答案相比,我更喜欢这个答案,因为繁琐的代码被移入函数中而不是内联。 (2认同)

Sil*_*olo 8

但是假设alpha实际上应该用于处理None的其他地方.

为了回应这个问题,我知道有一个类似None的值,实际上并不是None出于这个目的.

_novalue = object()

def alpha(p1=_novalue, p2=_novalue):
    if p1 is _novalue:
        p1 = "foo"
    if p2 is _novalue:
        p2 = "bar"
    print('{0},{1}'.format(p1, p2))
Run Code Online (Sandbox Code Playgroud)

现在参数仍然是可选的,所以你可以忽略它们中的任何一个.并且该功能None正确处理.如果你想明确地不传递参数,你可以传递_novalue.

>>> alpha(p1="FOO", p2=None)
FOO,None
>>> alpha(p1="FOO")
FOO,bar
>>> alpha(p1="FOO", p2=_novalue)
FOO,bar
Run Code Online (Sandbox Code Playgroud)

并且由于_novalue是为此明确目的而创建的特殊组合值,任何通过_novalue的人当然都打算采用"默认参数"行为,而不是None那些可能意图将该值解释为文字的人None.


a_g*_*est 5

您可以通过检查默认值alpha.__defaults__,然后使用它们而不是None。这样你就可以绕过默认值的硬编码:

>>> args = [None]
>>> alpha('FOO', *[x if x is not None else y for x, y in zip(args, alpha.__defaults__[1:])])
Run Code Online (Sandbox Code Playgroud)