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

2023-11-29

我见过很多次 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."

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


答案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

Yields:

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

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

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

因此,此实现会忽略第三个变量,即使它已被声明,因此这意味着该函数不再能够确定变量是否为“c” 被定义。

因此,对于我的用例,默认值为None不会完全成功。

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

  • 有没有办法将默认参数设置为等于另一个参数值?
  • Python 变量默认值的快捷方式是另一个变量值(如果它是 None)
  • 函数参数默认值等于另一个参数
  • 避免默认参数为空列表的 pythonic 方法是什么?
  • Python 可选函数参数默认为另一个参数的值

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


上面第一个链接中的评论提到使用_sentinel被定义为object(),它删除了 None 的使用,并将其替换为object()通过使用implied私人的sentinel。 (我简要地谈论了另一个类似的(但失败的)尝试Addendum.)


答案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输入,并且仅当未提供一个变量时 - 然后您将在位置复制另一个变量i.

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

因此,如果您正在寻找一种不会影响潜在输入的解决方案,则有效输入可以是None, object(), or _sentinel...然后(并且只有那时),在这一点上,我认为我的解决方案会有帮助。该技术的灵感来自于乔恩·克莱门茨回答的第二部分.


答案3:

我解决这个问题的方法是更改​​这个函数的命名,并且wrap该函数具有先前命名约定的函数,但我们不使用变量,而是使用*args。然后,您在本地范围内定义原始函数(使用新名称),并且只允许您想要的几种可能性。

步骤:

  1. 将函数重命名为类似的名称
  2. 删除可选参数的默认设置
  3. 开始在上面创建一个新函数,然后将原始函数添加到其中。
 def cast_to_string_concat(*args):
  1. 确定arity你的函数 - (我在搜索中发现了这个词......这是传递给给定函数的参数数量)
  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 """

所以,总而言之:

  • Answer 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)
  • Answer 2- 使用如果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)
  • Answer 3利用具有任意数量的包装函数寻找通用解决方案*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 ;)

谢谢阅读!

-Spencer


附录:接下来的三个链接建议使用某种类似的类或导入语句,但我选择不走这条路。如果这看起来像您想要的,请尝试一下!

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

  • 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/

留给读者的练习:

偏离这种技术,您可以直接断言c=object()然而,老实说,我还没有以这种方式为我工作。我的调查显示c == object() is False, and str(c) == str(object())也是False,这就是为什么我使用来自的实现马丁·彼得斯.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

默认情况下将参数设置为等于另一个参数的值 的相关文章

随机推荐