代码之家  ›  专栏  ›  技术社区  ›  lucidbrot

如果没有可选参数,则调用不带可选参数的函数

  •  24
  • lucidbrot  · 技术社区  · 7 年前

    有一个函数接受可选参数。

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

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

    >>> alpha()
    foo,bar
    >>> alpha("FOO")
    FOO,bar
    >>> alpha(p2="BAR")
    foo,BAR
    >>> alpha(p1="FOO", p2=None)
    FOO,None
    

    现在考虑一下我想称之为 alpha("FOO", myp2) myp2 将包含要传递的值,或者 None . 但即使函数处理 p2=None ,我希望它使用它的默认值 "bar" 相反。
    也许这句话有点让人困惑,所以让我改写一下:

    如果 myp2 is None ,呼叫 alpha("FOO") . 否则,呼叫 阿尔法(“foo”,MYP2) .

    区别是相关的,因为 alpha("FOO", None) 结果与 阿尔法(“FO”) .

    我怎样才能简明地(但可读地)作出这种区分?

    一种可能性通常是 check for None within alpha 这将得到鼓励,因为这将使代码更安全。但是假设 阿尔法 在其他需要处理的地方使用 没有 确实如此。

    我想在主叫方处理这个问题 .

    一种可能是区分案例:

    if myp2 is None:
        alpha("FOO")
    else:
        alpha("FOO", myp2)
    

    但当存在多个这样的参数时,这很快就会变成许多代码。(指数,2^n)

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

    我使用的是python 3.4,但是如果您的答案能够提供一种在任何python版本中都可以工作的好方法,那就更好了。


    这个问题在这里技术上已经完成了,但我再次修改了一些要求,因为第一个答案确实掩盖了这一点:
    我想要的行为 阿尔法 使用其默认值 "foo", "bar" 一般情况下保持不变,因此(可能)不能改变 阿尔法 本身。
    换句话说,假设 阿尔法 在其他地方用作 alpha(“foo”,无) 其中输出 FOO,None 是预期行为。

    6 回复  |  直到 7 年前
        1
  •  28
  •   deceze    7 年前

    将参数作为Kwargs从字典中传递,从中筛选出 None 价值观:

    kwargs = dict(p1='FOO', p2=None)
    
    alpha(**{k: v for k, v in kwargs.items() if v is not None})
    
        2
  •  9
  •   lucidbrot    7 年前

    虽然**绝对是一种语言特性,但它肯定不是为解决这个特定问题而创建的。你的建议行得通,我的也行。哪一个更有效取决于操作代码的其余部分。但是,仍然没有办法写 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)
    

    γ

    # 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
    

    这可能并不完美,但它似乎有效:它是一个可以用另一个函数和它的参数调用的函数,并且它适用于 deceze's answer 筛选出以下参数: None .

        3
  •  9
  •   Silvio Mayolo    7 年前

    但是假设alpha在其他地方被使用,在那里它实际上应该不会像处理它那样处理任何东西。

    为了回应这个问题,我已经知道 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))
    

    现在,参数仍然是可选的,所以您可以忽略传递其中任何一个参数。函数处理 没有 正确地。如果你想的话 明确地 不传递参数,可以传递 _novalue .

    >>> alpha(p1="FOO", p2=None)
    FOO,None
    >>> alpha(p1="FOO")
    FOO,bar
    >>> alpha(p1="FOO", p2=_novalue)
    FOO,bar
    

    从那时起 无价值 是为此创建的特殊补偿值吗 表达 目的,任何通过的人 无价值 当然是有意的“默认论点”行为,而不是某人通过 没有 谁可能想把这个值解释成文字 没有 .

        4
  •  2
  •   blue_note    7 年前

    不幸的是,没有办法做你想做的。即使广泛采用的Python库/框架也使用您的第一种方法。这是一个额外的代码行,但它是可读的。

    使用 alpha("FOO", myp2 or "bar") 方法,因为正如您所提到的,它创建了一种糟糕的耦合,因为它要求调用者了解函数的细节。

    关于解决方法:您可以为自己制作一个装饰器(使用 inspect 模块),它检查传递给它的参数。如果其中一个是 None ,它用自己的默认值替换该值。

        5
  •  2
  •   a_guest    7 年前

    您可以通过以下方式检查默认值: alpha.__defaults__ 然后用它们代替 None . 这样就避免了默认值的硬编码:

    >>> args = [None]
    >>> alpha('FOO', *[x if x is not None else y for x, y in zip(args, alpha.__defaults__[1:])])
    
        6
  •  1
  •   SV-97    7 年前

    我很惊讶没人提起这个

    def f(p1="foo", p2=None):
        p2 = "bar" if p2 is None else p2
        print(p1+p2)
    

    您不将任何项指定给p2作为标准(或者不指定,但是这样您在代码的某一点上就拥有了真正的标准),并使用一个内联if。在我看来,这是最能说明问题的答案。另一件让人想到的事情是使用一个包装器,但这会降低可读性。

    编辑: 我可能会用一个假人作为标准值并检查它。所以像这样:

    class dummy():
        pass
    
    def alpha(p1="foo", p2=dummy()):
        if isinstance(p2, dummy):
            p2 = "bar"
        print("{0},{1}".format(p1, p2))
    
    alpha()
    alpha("a","b")
    alpha(p2=None)
    

    生产:

    foo,bar
    a,b
    foo,None