关于python:默认情况下,将参数设置为等于另一个参数的值

Set argument equal to another argument's value by default

我见过很多次,在这里,Python程序员(包括我自己)希望给定函数中的一个变量在没有给定该值的情况下默认为另一个变量。

这是一个包含三个不同解决方案的演练,每个解决方案都增加了复杂性和健壮性。所以,向前!

这是给你的,如果你想说,我正在努力做到:

1
2
def my_fun(a, b, c=a):
  return str(a) + str(b) + str(c)

在这个例子中,如果没有给出C,那么我们将在末尾附加一个str(a)。这很简单,只是一个简单的玩具示例,我不怀疑您的用例可能更复杂。但是,这不是语法上正确的python,并且不会运行,因为没有定义a

1
2
3
Traceback (most recent call last):
  File"<input>", line 1, in <module>
NameError: name 'a' is not defined

然而,我经常看到以下问题的公认答案:

  • 不,这是不可能的
  • 您应该使用None的默认值,然后检查该值是否为None

如果这听起来像是你遇到的问题,我希望我能帮上忙!


答案1:

上面的解决方案如下:好的。

1
2
3
4
def cast_to_string_concat(a, b, c=None):
  c = a if c is None else c

  return str(a) + str(b) + str(c)

虽然这种方法可以解决大量的潜在问题(也许还有你的问题)!我想写一个函数,其中变量"EDOCX1"(0)的可能输入实际上是单例None,所以我需要做更多的挖掘。好的。

为了进一步解释这一点,使用以下变量调用函数:好的。

1
2
3
A='A'
B='B'
my_var = None

产量:好的。

1
2
cast_to_string_concat(A, B, my_var):
>>>'ABA'

然而,用户可能期望,因为他们使用三个变量调用函数,所以它应该打印三个变量,如下所示:好的。

1
2
cast_to_string_concat(A, B, my_var):
>>> 'ABNone' # simulated and expected outcome

因此,即使声明了第三个变量,这个实现也忽略了它,因此这意味着函数不再能够确定是否定义了变量"EDOCX1"〔0〕。好的。

因此,在我的用例中,默认值None并不能完全做到这一点。好的。

有关建议此解决方案的答案,请阅读以下内容:好的。

  • 是否可以将默认参数设置为其他参数值?
  • 变量默认值的python快捷方式为另一个变量值(如果没有)。
  • 函数参数的默认值等于另一个参数
  • 如何避免默认参数为空列表?
  • python可选函数参数,默认为另一个参数的值

但是,如果这对你不起作用,那就继续读吧!好的。

上述第一个链接中的注释提到使用由object()定义的_sentinel,它取消了使用none,并通过使用隐含的私有sentinel替换为object()。(我在附录中简要介绍了另一个类似的(但失败的)尝试。)好的。答案2:

1
2
3
4
5
_sentinel = object()
def cast_to_string_concat(a, b, c=_sentinel):
  c = a if c == _sentinel else c

  return str(a) + str(b) + str(c)
1
2
3
4
5
6
7
8
9
A='A'
B='B'
C='C'

cast_to_string_append(A,B,C)
>>> 'ABC'

cast_to_string_concat(A,B)
>>> 'ABA'

这真是太棒了!它能正确处理上面的边缘箱!自己看看:好的。

1
2
3
4
5
6
A='A'
B='B'
C = None

cast_to_string_concat(A, B, C)
>>> 'ABNone'

所以,我们结束了,对吧?是否有任何合理的方法可能不起作用?隐马尔可夫模型。。。可能不是!但我确实说过这是一个由三部分组成的答案,所以继续!;)好的。

为了完整性起见,让我们假设我们的程序在一个空间中运行,在这个空间中,每个可能的场景都是可能的。(这可能不是一个合理的假设,但我认为,只要有足够的关于计算机架构和对象选择实现的信息,就可以得出_sentinel的价值。所以,如果你愿意的话,让我们假设这确实是可能的,让我们设想一下,我们决定参照上述定义的_sentinel来检验这个假设。好的。

1
2
3
4
5
_sentinel = object()
def cast_to_string_concat(a, b, c=_sentinel):
  c = a if c == _sentinel else c

  return str(a) + str(b) + str(c)
1
2
3
4
5
6
A='A'
B='B'
S = _sentinel

cast_to_string_append(A,B,S)
>>> 'ABA'

等一下!我输入了三个参数,所以我应该看到三个参数的字符串连接在一起!好的。

*排队进入有不可预见后果的土地*好的。

我是说,实际上不是。回答:"这是微不足道的优势领域!或者它的同类是完全有保证的。好的。

这种感觉是对的!对于这个案例(可能是大多数案例),这真的不值得担心!好的。

但是,如果值得担心,或者你只是想在数学上满足于消除你所知道的所有边缘情况…向前的!好的。

好吧,经过长时间的锻炼,我们回来了!好的。

回想一下,目标是编写一个可能有n输入的函数,并且只有在没有提供一个变量的情况下,您才会复制位置i中的另一个变量。好的。

如果我们改变方法允许任意数量的变量,而不是默认地定义变量呢?好的。

因此,如果您正在寻找一种不影响潜在输入的解决方案,其中有效输入可以是Noneobject()_sentinel…然后(只有在那时),在这一点上,我认为我的解决方案将是有帮助的。这项技术的灵感来自乔恩·克莱门茨回答的第二部分。好的。答案3:

我对这个问题的解决方案是更改这个函数的命名,并用一个以前命名约定的函数包装这个函数,但是我们使用*args,而不是使用变量。然后在本地范围内定义原始函数(使用新名称),并且只允许您希望的几种可能性。好的。

在步骤中:好的。

  • 将函数重命名为类似的内容
  • 删除可选参数的默认设置
  • 开始在上面创建一个新函数,并在中标记原始函数。
  • 1
     def cast_to_string_concat(*args):
  • 确定你的函数的arity(我在搜索中找到了那个词…这是传递给给定函数的参数数目)
  • 使用内部的case语句来确定是否输入了有效数量的变量,并相应地进行调整!
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def cast_to_string_append(*args):

        def string_append(a, b, c):
            # this is the original function, it is only called within the wrapper
            return str(a) + str(b) + str(c)

        if len(args) == 2:
            # if two arguments, then set the third to be the first
            return string_append(*args, args[0])

        elif len(args) == 3:
            # if three arguments, then call the function as written
            return string_append(*args)

        else:
            raise Exception(f'Function: cast_to_string_append() accepts two or three arguments, and you entered {len(args)}.')

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # instantiation

    A='A'
    B='B'
    C='C'
    D='D'

    _sentinel = object()
    S = _sentinel

    N = None
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    """ Answer 3 Testing"""

    # two variables

    cast_to_string_append(A,B)

    >>> 'ABA'


    # three variables

    cast_to_string_append(A,B,C)

    >>> 'ABC'


    # three variables, one is _sentinel

    cast_to_string_append(A,B,S)

    >>>'AB<object object at 0x10c56f560>'


    # three variables, one is None

    cast_to_string_append(A,B,N)

    >>>'ABNone'


    # one variable

    cast_to_string_append(A)

    >>>Traceback (most recent call last):
    >>>  File"<input>", line 1, in <module>
    >>>  File"<input>", line 13, in cast_to_string_append
    >>>Exception: Function: cast_to_string_append() accepts two or three arguments, and you entered 1.

    # four variables

    cast_to_string_append(A,B,C,D)

    >>>Traceback (most recent call last):
    >>>  File"<input>", line 1, in <module>
    >>>  File"<input>", line 13, in cast_to_string_append
    >>>Exception: Function: cast_to_string_append() accepts two or three arguments, and you entered 4.


    # ten variables

    cast_to_string_append(0,1,2,3,4,5,6,7,8,9)

    >>>Traceback (most recent call last):
    >>>  File"<input>", line 1, in <module>
    >>>  File"<input>", line 13, in cast_to_string_append
    >>>Exception: Function: cast_to_string_append() accepts two or three arguments, and you entered 10.


    # no variables

    cast_to_string_append()

    >>>Traceback (most recent call last):
    >>>  File"<input>", line 1, in <module>
    >>>  File"<input>", line 13, in cast_to_string_append
    >>>Exception: Function: cast_to_string_append() accepts two or three arguments, and you entered 0.

    """ End Answer 3 Testing"""

    。所以,总而言之:

    • 答案1-最简单的答案,适用于大多数情况。
    1
    2
    3
    4
    def cast_to_string_concat(a, b, c=None):
      c = a if c is None else c

      return str(a) + str(b) + str(c)
    • 回答2—如果None实际上没有通过_sentinel切换到object()表示空参数,则使用。
    1
    2
    3
    4
    5
    _sentinel = object()
    def cast_to_string_concat(a, b, c=_sentinel):
      c = a if c == _sentinel else c

      return str(a) + str(b) + str(c)

    • 答案3:使用*args寻找一个使用任意数量包装函数的一般解决方案,并处理可接受的内部情况:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def cast_to_string_append(*args):

        def string_append(a, b, c):
            # this is the original function, it is only called within the wrapper
            return str(a) + str(b) + str(c)

        if len(args) == 2:
            # if two arguments, then set the third to be the first
            return string_append(*args, args[0])

        elif len(args) == 3:
            # if three arguments, then call the function as written
            return string_append(*args)

        else:
            raise Exception(f'Function: cast_to_string_append() accepts two or three arguments, and you entered {len(args)}.')

    正如我最喜欢的计算机科学教授James Cain博士,一位非常清晰地提倡安全性和完整性的人所说,"计算是上下文的(sic)",所以一定要使用对你有用的东西!但对我来说,我将使用选项3;)好的。

    谢谢你的阅读!好的。

    -斯宾塞好的。

    附录:接下来的三个链接建议使用类或导入语句的一些外观,但我选择了不走这条路。如果这看起来像你想要的,那就试试吧!好的。

    For answers utilizing a class, read these:

    Ok.

    • https://www.oreilly.com/library/view/python-cookbook/0596001673/ch17s02.html
    • https://www.oreilly.com/library/view/python-cookbook/0596001673/ch05s24.html
    • https://www.revsys.com/tidbits/sentinel-values-python/

    留给读者的练习:好的。

    Deviating from this technique, you can directly assert c=object(), however, in honesty, I haven't gotten that way to work for me. My investigation shows c == object() is False, and str(c) == str(object()) is also False, and that's why I'm using the implementation from Martin Pieters.

    Ok.

    好啊。