默认设置参数等于另一个参数的值 [英] Set argument equal to another argument's value by default

查看:49
本文介绍了默认设置参数等于另一个参数的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经多次看到 Python 程序员(包括我自己)希望给定函数中的变量在未给定值的情况下默认为另一个变量.

这是一个演练,其中包含三种不同的问题解决方案,每一种都增加了复杂性和稳健性.那么,继续!

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

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

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

回溯(最近一次调用):文件<input>",第 1 行,在 <module> 中.NameError: 名称 'a' 未定义

但是,我最常看到的答案是:

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

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

解决方案

答案 1:

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

def cast_to_string_concat(a, b, c=None):c = a if c is None else c返回 str(a) + str(b) + str(c)

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

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

A='A'乙=‘乙’my_var = 无

产量:

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

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

cast_to_string_concat(A, B, my_var):>>>'ABNone' # 模拟和预期结果

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

因此,对于我的用例,None 的默认值不会完全奏效.

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


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


上面第一个链接中的注释提到使用 object() 定义的 _sentinel,它删除了 None 的使用,并用 object 替换它() 通过使用隐含私有哨兵代码>.(我在附录中简要介绍了另一个类似(但失败)的尝试.)


答案 2:

_sentinel = object()def cast_to_string_concat(a, b, c=_sentinel):c = a if c == _sentinel else c返回 str(a) + str(b) + str(c)

A='A'乙=‘乙’C='C'cast_to_string_append(A,B,C)>>>'ABC'cast_to_string_concat(A,B)>>>'ABA'


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


A='A'乙=‘乙’C = 无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返回 str(a) + str(b) + str(c)

A='A'乙=‘乙’S = _哨兵cast_to_string_append(A,B,S)>>>'ABA'


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


*排队进入后果不堪设想的土地*

我的意思是,实际上不是.回应:这是可以忽略不计的边缘案例领域!!"或者它的同类是完全有保证的.

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

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


好的,经过长时间的练习,我们回来了!

回想一下,我们的目标是编写一个可能有 n 个输入的函数,并且仅当未提供一个变量时 - 然后您将在 i 位置复制另一个变量.

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

因此,如果您正在寻找一种不会影响潜在输入的解决方案,则有效输入可以是 Noneobject()>_sentinel ...然后(并且只有那时),在这一点上,我认为我的解决方案会有所帮助.该技术的灵感来自Jon Clements 回答的第二部分.


答案 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):# 这是原始函数,仅在包装器中调用返回 str(a) + str(b) + str(c)如果 len(args) == 2:# 如果有两个参数,则将第三个设置为第一个返回 string_append(*args, args[0])elif len(args) == 3:# 如果三个参数,则调用函数返回 string_append(*args)别的:raise Exception(f'Function: cast_to_string_append() 接受两个或三个参数,并且您输入了 {len(args)}.')

# 实例化A='A'乙=‘乙’C='C'D='D'_哨兵=对象()S = _哨兵N = 无

"""答案 3 测试"# 两个变量cast_to_string_append(A,B)>>>'ABA'# 三个变量cast_to_string_append(A,B,C)>>>'ABC'# 三个变量,一个是_sentinelcast_to_string_append(A,B,S)>>>'AB


所以,总结一下:

  • 答案 1 - 最简单的答案,适用于大多数情况.

def cast_to_string_concat(a, b, c=None):c = a if c is None else c返回 str(a) + str(b) + str(c)

  • 答案 2 - 如果 None 实际上没有通过 _sentinel<切换到 object() 来表示空参数,则使用/code> .

_sentinel = object()def cast_to_string_concat(a, b, c=_sentinel):c = a if c == _sentinel else c返回 str(a) + str(b) + str(c)

  • 答案 3 使用 *args 寻找使用具有任意元数的包装函数的通用解决方案,并处理内部可接受的情况:

def cast_to_string_append(*args):def string_append(a, b, c):# 这是原始函数,仅在包装器中调用返回 str(a) + str(b) + str(c)如果 len(args) == 2:# 如果有两个参数,则将第三个设置为第一个返回 string_append(*args, args[0])elif len(args) == 3:# 如果三个参数,则调用函数返回 string_append(*args)别的:raise Exception(f'Function: cast_to_string_append() 接受两个或三个参数,并且您输入了 {len(args)}.')

正如我最喜欢的计算机科学教授 James Cain 博士所说,他是最高安全性和完整性的明确倡导者,他说:计算是上下文 [原文如此]",所以请始终使用适合您的方法!但对我来说,我将使用选项 3 ;)

感谢阅读!

-斯宾塞


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

<块引用>

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

练习留给读者:

<块引用><块引用>

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

I've seen many times where Python programmers (myself included) want a variable in a given function to default to another variable if that value is not given.

This is designed as a walkthrough with three different solutions to the problem, each of increasing complexity and robustness. So, onward!

This is for you, if you would say, I'm trying to do this:

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

In this example, if c is not given, then we would append a str(a) to the end. This is simple, and just a simple toy example, and I do not doubt your use case is likely much more complicated. However, this is not syntactically correct Python, and it will not run, as a is not defined.

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

However, most often I see the accepted answers of:

  • "No, this is not possible"
  • "You should use the default value of None, then check if the value is None."

If this sounds like a problem you've encountered, I hope I can help!

解决方案

Answer 1:

The solution from above looks like this:

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

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

While this approach will solve a myriad of potential problems, (and maybe yours)! I wanted to write a function where a possible input for variable "c" is indeed the singleton None, so I had to do more digging.

To explain that further, calling the function with the following variables:

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

Yields:

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

Whereas the user might expect that since they called the function with three variables, then it should print the three variables, like this:

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

So, this implementation ignores the third variable, even when it was declared, so this means the function no longer has the ability to determine whether or not variable "c" was defined.

So, for my use case, a default value of None would not quite do the trick.

For the answers that suggest this solution, read these:


But, if that doesn't work for you, then maybe keep reading!


A comment in the first link above mentions using a _sentinel defined by object(), which removes the use of None, and replaces it with the object() through using the implied private sentinel. (I briefly talk about another similar (but failed) attempt in the Addendum.)


Answer 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'


So this is pretty awesome! It correctly handles the above edge case! See for yourself:


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

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

So, we're done, right? Is there any plausible way that this might not work? Hmm... probably not! But I did say this was a three-part answer, so onward! ;)


For the sake of completeness, let's imagine our program operates in a space where every possible scenario is indeed possible. (This may not be a warranted assumption, but I imagine that one could derive the value of _sentinel with enough information about the computer's architecture and the implementation of the choice of the object. So, if you are willing, let us assume that is indeed possible, and let's imagine we decide to test that hypothesis referencing _sentinel as defined above.


_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'


Wait a minute! I entered three arguments, so I should see the string concatenation of the three of them together!


*queue entering the land of unforeseen consequences*

I mean, not actually. A response of: "That's negligible edge case territory!!" or its ilk is perfectly warranted.

And that sentiment is right! For this case (and probably most cases) this is really not worth worrying about!

But if it is worth worrying about, or if you just want the mathematical satisfaction of eliminating all edge cases you're aware of ... onward!


Okay, after that long exercise, we're back!

Recall the goal is to write a function that could potentially have n inputs, and only when one variable is not provided - then you will copy another variable in position i.

Instead of defining the variable by default, what if we change the approach to allow an arbitrary number of variables?

So if you're looking for a solution that does not compromise on potential inputs, where a valid input could be either None, object(), or _sentinel ... then (and only then), at this point, I'm thinking my solution will be helpful. The inspiration for the technique came from the second part of Jon Clements' answer.


Answer 3:

My solution to this problem is to change the naming of this function, and wrap this function with a a function of the previous naming convention, but instead of using variables, we use *args. You then define the original function within the local scope (with the new name), and only allow the few possibilities you desire.

In steps:

  1. Rename function to something similar
  2. Remove the default setup for your optional parameter
  3. Begin to create a new function just above and tab the original function in.

 def cast_to_string_concat(*args):

  1. Determine the the arity of your function - (I found that word in my search... that is the number of the parameters passed into a given function)
  2. Utilize a case statement inside that determines if you entered a valid number of variables, and adjust accordingly!

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 """


So, in summary:

  • Answer 1 - the simplest answer, and works for most cases.

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 - use if None does not actually signify an empty parameter by switching to object() , through _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 seeks out a general solution utilizing a wrapper function with arbitrary arity using *args, and handles the acceptable cases inside:

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)}.')

As my favorite computer science professor Dr. James Cain, an articulate advocate of utmost security and completeness, said, "Computing is contextural [sic]", so always use what works for you! But for me, I'll be using Option 3 ;)

Thanks for reading!

-Spencer


Addendum: The next three links suggest using some semblance of a class or import statement, but I chose not to go down this route. If this looks like what you're after, give it a go!

For answers utilizing a class, read these:

Exercise left to reader:

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.

这篇关于默认设置参数等于另一个参数的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆