【问题标题】:Is there a way to find an item in a tuple without using a for loop in Python?有没有一种方法可以在不使用 Python 中的 for 循环的情况下在元组中查找项目?
【发布时间】:2011-12-06 23:22:33
【问题描述】:

我有一个 Control 值的元组,我想找到一个名称匹配的元组。现在我用这个:

listView
for control in controls:
    if control.name == "ListView":
        listView = control

我能比这更简单吗?也许是这样的:

listView = controls.FirstOrDefault(c => c.name == "ListView")

【问题讨论】:

    标签: python loops tuples


    【解决方案1】:

    这是一种选择:

    listView = next(c for c in controls if c.name == "ListView")
    

    请注意,如果不存在匹配项,这将引发 StopIteration,因此您需要将其放入 try/except 并在获得 StopIteration 时将其替换为默认值。

    或者,您可以将默认值添加到可迭代对象中,以便 next 调用始终成功。

    from itertools import chain
    listView = next(chain((c for c in controls if c.name == "ListView"), [default])
    

    如果您使用的是 Python 2.5 或更低版本,请将调用从 next(iterable) 更改为 iterable.next()

    【讨论】:

      【解决方案2】:

      纯粹出于好奇,我将自己的答案与您的原始代码和 F.J. 的解决方案相结合,以进行性能对比测试。

      您的解决方案似乎是所有解决方案中最快的。我的解决方案检查控件元组的所有可能元素,因此随着元组大小的增加它会变慢。

      代码如下:

      from timeit import Timer as T
      from itertools import chain, dropwhile
      
      class control(object):
          def __init__(self, name):
              self.name = name
      
      def johan_venge(tuple_):
          for el in tuple_:
              if el.name == 'foobar':
                  return el
          return None
      
      def mac(tuple_):
          return filter(lambda x : x.name == 'foobar', tuple_)[0]
      
      def mac2(tuple_):
          return list(dropwhile(lambda x : x.name != 'foobar', tuple_))[0]
      
      def fj(tuple_):
          return next(c for c in tuple_ if c.name == 'foobar')
      
      def fj2(tuple_):
          return next(chain((c for c in tuple_ if c.name == 'foobar')))
      
      if __name__ == '__main__':
          REPS = 10000
          controls = (control('hello'), control('world'), control('foobar'))
          print T(lambda : johan_venge(controls)).repeat(number = REPS)
          print T(lambda : mac(controls)).repeat(number = REPS)
          print T(lambda : mac2(controls)).repeat(number = REPS)
          print T(lambda : fj(controls)).repeat(number = REPS)
          print T(lambda : fj2(controls)).repeat(number = REPS)    
      

      这是我系统上的输出:

      [0.005961179733276367, 0.005975961685180664, 0.005918025970458984]
      [0.013427019119262695, 0.013586044311523438, 0.013450145721435547]
      [0.024325847625732422, 0.0254058837890625, 0.02396702766418457]
      [0.014491081237792969, 0.01442408561706543, 0.01484990119934082]
      [0.01691603660583496, 0.016616106033325195, 0.016437053680419922]
      

      HTH! :)

      【讨论】:

      • 感谢您的测试。顺便说一句,我不确定如何解释输出:O 你有相同方法的结果,但每个方法的结果不同,逐行?
      • @JoanVenge - 查看timeit.Timer.repeat 的文档。基本上,对于每个解决方案,每次运行 10.000 次迭代的三个运行。
      • 感谢 mac,现在它确实有意义了。这可能是基本的,但我注意到您使用了',但其他答案使用",这有什么不同吗?
      • 我同意,它看起来确实不那么杂乱:O
      【解决方案3】:
      listView = filter(lambda c: c.name=="ListView", controls)[0]
      

      如果不存在这样的控件,则抛出 IndexError

      有点深奥,但不需要try/except:

      listView = (lambda x: x[0] if x else None)(filter(lambda c: c.name=="ListView", controls))
      

      【讨论】:

      • 我们基本上同时发布了相同的解决方案!如果您有兴趣了解它与其他人相比有多快,请查看我的答案! :)
      • 可以像 FJ 的回答那样修改它,这样它就不会在不使用 try/catch 的情况下引发异常?只是好奇。
      • @JoanVenge - 如果你把它分成两行,你可以这样做:仅限第一行 filt = filter(...。第二行:return filt[0] if len(filtered) > 0 else None.
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-16
      • 1970-01-01
      • 1970-01-01
      • 2011-08-15
      • 2021-07-12
      • 2012-07-24
      • 1970-01-01
      相关资源
      最近更新 更多