我已经见过很多次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
但是,最常见的是,我看到以下答案:
None
,然后检查该值是否为None
。” 如果这听起来像是您遇到的问题,希望我能为您提供帮助!
答案 0 :(得分:2)
上面的解决方案如下:
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
定义的object()
,它删除了None的使用,并通过使用{{3}将其替换为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)
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
输入的函数,并且只有在未提供一个变量的情况下,您才能在位置i
复制另一个变量。
代替默认定义变量,如果我们改变方法以允许任意数量的变量,该怎么办?
因此,如果您正在寻找一种不影响潜在输入的解决方案,那么有效输入可以是None
,object()
或_sentinel
...而且只有这样),在这一点上,我认为我的解决方案会有所帮助。该技术的灵感来自implied。
我对这个问题的解决方案是更改此函数的命名,并使用先前命名约定的函数包装该函数,但是我们使用*args
而不是使用变量。然后,您在本地范围内(使用新名称)定义原始功能,并且只允许您希望的几种可能性。
步骤:
def cast_to_string_concat(*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)}.')
# 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 """
def cast_to_string_concat(a, b, c=None):
c = a if c is None else c
return str(a) + str(b) + str(c)
None
通过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)
*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)博士(他是最高安全性和完整性的明确倡导者)所说:“计算是上下文[sic]”,所以请始终使用对您有用的东西!但是对我来说,我将使用选项3;)
感谢您的阅读!
-喷枪
附录: 接下来的三个链接建议使用类相似的形式或import语句,但我选择不遵循这条路线。如果这看起来像您所追求的,请尝试一下!
有关使用课程的答案,请阅读以下内容:
锻炼留给读者:
与这种技术不同,您可以直接声明
c=object()
,但是,老实说,我还没有为我工作的那种方法。我的调查表明c == object()
是False
,而str(c) == str(object())
也是False
,这就是为什么我使用https://www.revsys.com/tidbits/sentinel-values-python/中的实现。