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

标签 python function variables default assign

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


产量:

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


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

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


因此,此实现即使在声明了第三个变量时也将忽略它,因此这意味着该函数不再具有确定是否定义了变量“ c”的能力。

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

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


Is there a way to set a default parameter equal to another parameter value?
Python shortcut for variable default value to be another variable value if it is None
Function argument's default value equal to another argument
What is the pythonic way to avoid default parameters that are empty lists?
Python optional function argument to default to another argument's value




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



上面第一个链接中的注释提到使用由_sentinel定义的object(),它删除了None的使用,并通过使用implied私有object()将其替换为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输入的函数,并且仅在未提供一个变量的情况下-您将在位置i中复制另一个变量。

如果不更改默认值来定义变量,那么如果我们改变方法以允许任意数量的变量呢?

因此,如果您正在寻找一种不影响潜在输入的解决方案,其中有效输入可以是Noneobject()_sentinel ...,那么(并且只有那时),我认为我的解决方案会有所帮助。该技术的灵感来自second part of Jon Clements' answer



答案三:

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

步骤:


将函数重命名为类似的名称
删除可选参数的默认设置
开始在上方创建一个新功能,然后在其中输入原始功能。


 def cast_to_string_concat(*args):



确定您的函数的通用性-(我在搜索中发现了那个单词……这是传递给给定函数的参数数量)
利用内部的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-如果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)



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

谢谢阅读!

-喷枪



附录:接下来的三个链接建议使用类的相似性或import语句,但我选择不遵循这条路线。如果这看起来像您想要的,请尝试一下!


  有关使用课程的答案,请阅读以下内容:
  
  
  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()Falsestr(c) == str(object())也是False,这就是为什么我使用Martin Pieters中的实现的原因。

关于python - 默认情况下,将参数设置为等于另一个参数的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55508923/

相关文章:

c - 如何在 C 中通过 for 循环定义多个整数变量?

Mysql程序: SELECT and UPDATE

php - 我想在 Insert 语句中将当前的 "UserID"传递到数据库中。 php/mysql

python - djangocms-installer 不询问设置问题

python - .obj 到 python 中带有法线的原始顶点转换器

c++ - 在 C++ 中同时执行多个函数

Javascript - 为 onClick 事件调用函数

python - 使用python3.6类型错误: an integer is required

python - Matplotlib 颜色图范围

c++ - 没有匹配函数调用 'AnimationSet::AnimationSet()'