【问题标题】:Python idiom to return the only element or NonePython成语返回唯一的元素或无
【发布时间】:2017-03-27 22:14:25
【问题描述】:

我有一个列表,它可能包含也可能不包含满足给定谓词的唯一元素。我正在寻找一个表达式,如果它存在 并且 是唯一的,则它评估为满足该谓词的项目,否则返回 None。类似的东西

numbers = [4, 3, 9, 7, 1, 2, 8]
print(the(item for item in numbers if item > 10))     # None
print(the(item for item in numbers if item % 2 == 0)) # None
print(the(item for item in numbers if item % 7 == 0)) # 7

是否有一个内置的习惯用法,还是我必须编写自己的the 函数?

【问题讨论】:

    标签: python idioms


    【解决方案1】:

    我不知道一种表达方式,但那个简单的函数应该可以工作:

    def the(it, cond):
        l = [ i for i in it if cond(i) ]
        return l[0] if len(l) == 1 else None
    

    测试:

    >>> print(the(numbers,(lambda x: x > 10)))
    None
    >>> print(the(numbers,(lambda x: x % 7 == 0)))
    7
    >>> print(the(numbers,(lambda x: x % 2 == 0)))
    None
    

    【讨论】:

    • 可以是使用(l + [None])[::2][-1]((l + [None]) * 3)[2] 的单一表达式,但我真的不认为它们很好:-)
    【解决方案2】:

    您可以尝试使用islice 请求两个元素,会更简单一些:

    def the(it):
        tmp = list(islice(it, 2))
        return tmp[0] if len(tmp) == 1 else None
    

    或者循环:

    def the(it):
        value = None
        for i, value in enumerate(it):
            if i == 1:
                return None
        return value
    

    或者另一种使用next的方式:

    def the(it):
        first = next(it, None)
        o = object()
        if next(it, o) is o:
            return first
    

    或与您的相似:

    def the(it):
        first = next(it, None)
        try:
            next(it)
        except:
            return first
    

    【讨论】:

      【解决方案3】:

      为了记录,我可以写the

      def the(it):
          it = iter(it)
          try:
              value = next(it)
          except StopIteration:
              return None
          try:
              next(it)
          except StopIteration:
              return value
          return None
      

      【讨论】:

        【解决方案4】:

        这应该是这个问题的简单解决方案:

        def the(items):
            return items[0] if (len(items) == 1) else None
        
        numbers = [4, 3, 9, 7, 1, 2, 8]
        print(the([item for item in numbers if item > 10]))     # None
        print(the([item for item in numbers if item % 2 == 0])) # None
        print(the([item for item in numbers if item % 7 == 0])) # 7
        

        您还可以应用以下格式。就代码而言,它可能不会更简单,但是当项目数量很大时,它肯定会更快。

        print(the(list(filter(lambda x: x > 10, numbers))))     # None
        print(the(list(filter(lambda x: x % 2 == 0, numbers)))) # None
        print(the(list(filter(lambda x: x % 7 == 0, numbers)))) # 7
        

        【讨论】:

          【解决方案5】:

          您可以使用以下惰性方法。从生成器表达式中获取接下来的两项,如果生成器中的第二项不是None,则返回None

          def func(lst, pred):
              gen = (i for i in lst if pred(i))
              v, w = next(gen, None), next(gen, None)
              return v if w is None else None
          
          
          print(func([4, 3, 9, 7, 1, 2, 8], lambda x: x>10))
          # None
          print(func([4, 3, 9, 7, 1, 2, 8], lambda x: x % 2 == 0))
          # None
          print(func([4, 3, 9, 7, 1, 2, 8], lambda x: x % 7 == 0))
          # 7
          print(func([4, 3, 9, 0, 1, 2, 8], lambda x: x % 7 == 0))
          # 0
          

          【讨论】:

          • 使用集合将删除重复项。我假设print(the(item for item in (1,1,2,2) if item % 2 == 0)) 应该是None
          • @TigerhawkT3 没有考虑到这一点。更新
          • print(func([1, None], lambda x: True)) 怎么样(打印 1 而不是 None)。
          • @StefanPochmann 他们可以使用哨兵代替None,我强烈怀疑他们的号码列表中是否有None
          • 不过,这只是他们的例子。他们的问题标题和文字说“元素”,他们自己的答案以及您的答案看起来很笼统。顺便说一句,您已经有一个可以用作哨兵的对象:gen。我认为那会很酷:-)
          猜你喜欢
          • 2021-04-30
          • 1970-01-01
          • 2014-07-16
          • 1970-01-01
          • 2018-12-25
          • 1970-01-01
          • 2019-07-06
          • 2020-08-16
          • 1970-01-01
          相关资源
          最近更新 更多