42

例如,我有一个返回排列列表的基本方法。

import itertools
def perms(elements, set_length=elements):
    data=[]
    for x in range(elements):
        data.append(x+1)
    return list(itertools.permutations(data, set_length))

现在我明白了,在当前状态下,这段代码不会运行,因为第二个elements没有定义,但是有没有优雅的方式来完成我在这里尝试做的事情?如果还不清楚,我想让默认setLength值等于传入的第一个参数。谢谢。

4

4 回答 4

56

不,函数关键字参数默认值是在定义函数时确定的,而不是在执行函数时确定的。

将默认值设置为None并检测到:

def perms(elements, setLength=None):
    if setLength is None:
        setLength = elements

如果您需要能够指定None为参数,请使用不同的哨兵值:

_sentinel = object()

def perms(elements, setLength=_sentinel):
    if setLength is _sentinel:
        setLength = elements

现在调用者可以设置setLengthNone并且它不会被视为默认值。

于 2013-06-17T21:38:16.560 回答
8

由于 Python 处理绑定和默认参数的方式......

标准方法是:

def perms(elements, setLength=None):
    if setLength is None:
        setLength = elements

另一种选择是:

def perms(elements, **kwargs):
    setLength = kwargs.pop('setLength', elements)

perms(elements, setLength='something else')尽管如果您不想要默认值,这需要您明确使用...

于 2013-06-17T21:37:49.820 回答
7

您应该执行以下操作:

def perms(elements,setLength=None):
    if setLength is None:
        setLength = elements
于 2013-06-17T21:38:28.090 回答
3

答案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定义的object()

因此,此解决方案删除了​​ None 的使用,并使用隐含的private将其替换为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'

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


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

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

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

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

练习留给读者:

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


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

回想一下,我们的目标是编写一个可能有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使用具有任意数量的包装函数寻找通用解决方案*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)}.')

使用对你有用的东西!但对我来说,我将使用选项 3 ;)

于 2019-04-04T05:52:35.167 回答