首页 > 解决方案 > 默认情况下设置参数等于另一个参数的值

问题描述

我曾多次看到 Python 程序员(包括我自己)希望给定函数中的变量默认为另一个变量(如果未给出该值)。

这被设计为一个演练,包含三个不同的问题解决方案,每个解决方案都增加了复杂性和鲁棒性。所以,前进!

这是给你的,如果你想说,我正在尝试这样做:

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

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

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

但是,我经常看到以下公认的答案:

如果这听起来像您遇到的问题,希望我能提供帮助!

标签: pythonfunctionvariablesdefaultassign

解决方案


答案1:

上面的解决方案如下所示:

def cast_to_string_concat(a, b, c=None):
  c = a if c is None else c

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

虽然这种方法将解决无数潜在的问题,(也许是你的)!我想编写一个函数,其中变量“ c”的可能输入确实是单例None,所以我不得不做更多的挖掘。

为了进一步解释,使用以下变量调用函数:

A='A'
B='B'
my_var = None

产量:

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

而用户可能期望,由于他们使用三个变量调用函数,那么它应该打印三个变量,如下所示:

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

所以,这个实现忽略了第三个变量,即使它被声明了,所以这意味着函数不再有能力确定变量“ c”是否被定义。

因此,对于我的用例,默认值None并不能完全解决问题。

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


但是,如果这对您不起作用,那么也许继续阅读!


上面第一个链接中的评论提到使用_sentinel定义的 by ,它删除了 None 的使用,并使用隐含的privateobject()将其替换为object()through 。(我在附录中简要讨论了另一个类似(但失败)的尝试。)sentinel


答案 2:

_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)
A='A'
B='B'
C='C'

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

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

所以这太棒了!它正确处理了上述边缘情况!你自己看:


A='A'
B='B'
C = None

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

所以,我们完成了,对吧?有什么合理的方法可以使这不起作用?嗯……应该不会吧!但我确实说过这是一个由三部分组成的答案,所以继续!;)


为了完整起见,让我们假设我们的程序在一个所有可能的场景都确实可能的空间中运行。(这可能不是一个有根据的假设,但我想可以_sentinel通过有关计算机体系结构和对象选择的实现的足够信息来推导出 的值。所以,如果您愿意,让我们假设这确实是可能的,让我们假设我们决定测试_sentinel上面定义的假设引用。


_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)
A='A'
B='B'
S = _sentinel

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

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


*排队进入无法预料后果的土地*

我的意思是,实际上不是。回应:“那是可以忽略不计的边缘案例领域!!” 或者它的同类是完全有根据的。

这种情绪是对的!对于这种情况(可能是大多数情况),这真的不值得担心!

但是,如果值得担心,或者如果你只是想要消除所有你知道的边缘情况的数学满足......继续!


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

回想一下,我们的目标是编写一个可能有n输入的函数,并且只有在没有提供一个变量的情况下——然后您将在 position 中复制另一个变量i

如果我们改变方法以允许任意数量的变量,而不是默认定义变量,该怎么办?

因此,如果您正在寻找一种不会影响潜在输入的解决方案,其中有效输入可能是None,object()_sentinel... 那么(并且只有这样),此时,我认为我的解决方案会有所帮助. 该技术的灵感来自Jon Clements 回答的第二部分


答案 3:

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

在步骤:

  1. 将函数重命名为类似的东西
  2. 删除可选参数的默认设置
  3. 开始在上面创建一个新函数并在其中添加原始函数。
 def cast_to_string_concat(*args):
  1. 确定你的函数的数量- (我在搜索中找到了那个词......这是传递给给定函数的参数的数量)
  2. 利用里面的 case 语句来确定您是否输入了有效数量的变量,并相应地进行调整!
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)}.')

# instantiation

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

_sentinel = object()
S = _sentinel

N = None
""" 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 - 最简单的答案,适用于大多数情况。
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 - 使用 ifNone实际上并不表示通过切换到空object()参数_sentinel
_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寻求使用具有任意数量的包装函数的通用解决方案 using *args,并在内部处理可接受的情况:
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 博士所说,他是最高安全性和完整性的明确倡导者,他说:“计算是上下文相关的 [原文如此]”,所以请始终使用适合您的方法!但对我来说,我将使用选项 3 ;)

谢谢阅读!

-斯宾塞


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

有关使用课程的答案,请阅读以下内容:

练习留给读者:

偏离这种技术,您可以直接断言c=object(),但是,老实说,我还没有为我工作的那种方式。我的调查显示c == object()Falsestr(c) == str(object())也是False,这就是我使用Martin Pieters的实现的原因。


推荐阅读