【问题标题】:Is there a way to set a default parameter equal to another parameter value?有没有办法将默认参数设置为等于另一个参数值?
【发布时间】:2013-06-17 21:36:00
【问题描述】:

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

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 值等于传入的第一个参数。谢谢。

【问题讨论】:

标签: python python-3.x


【解决方案1】:

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

将默认设置为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

现在调用者可以将setLength 设置为None,它不会被视为默认值。

【讨论】:

  • 在第一种(无)情况下,您可以使用单线 setLength = setLength or elements 代替 if 语句 - 如果 setLength 有一个非无值,它会保留该值,否则它会得到设置为elements。 (这看起来是否合理是个人(或团队)偏好的问题,对我来说它读起来没问题。)
  • @yoyo:这取决于setLength=0 是否为有效值,因此如果您需要接受虚假值,这不是偏好问题。
  • @Martjin,公平点,它只有在任何“假”的味道都不是有效值的情况下才有效。只要这是真的,偏好的问题就是语句的外观和阅读方式,有些人可能不喜欢这种措辞。
【解决方案2】:

由于 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'),如果您不想要默认值...

【讨论】:

    【解决方案3】:

    你应该这样做:

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

    【讨论】:

      【解决方案4】:

      答案 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 的默认值并不能完全解决问题。

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


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


      上面第一个链接中的评论提到使用由object() 定义的_sentinel

      因此,此解决方案删除了​​ None 的使用,并通过使用 implied 私有 sentinel 将其替换为 object()


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

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

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


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

      所以,总结一下:

      • 答案 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 实际上并不表示空参数,则通过 _sentinel 切换到 object() 使用。
      _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 ;)

      【讨论】:

      • 选项 3 有一个巨大的缺点:使用您的函数时,我看不到您实际期望的变量。这在使用 IDE 进行日常编程时非常重要。
      • 绝对正确!不用争论。
      猜你喜欢
      • 1970-01-01
      • 2019-08-25
      • 1970-01-01
      • 2018-01-22
      • 1970-01-01
      • 2017-02-03
      • 2023-03-11
      • 1970-01-01
      • 2015-11-05
      相关资源
      最近更新 更多