Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/redis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 滥用产量以避免循环中的条件_Python_Performance_Generator_Yield - Fatal编程技术网

Python 滥用产量以避免循环中的条件

Python 滥用产量以避免循环中的条件,python,performance,generator,yield,Python,Performance,Generator,Yield,我需要搜索其他事物中第一个、最后一个、任何或所有出现的事物。为了避免重复我自己()我提出了以下解决方案 有趣的是search\u revisions()和collect\u one\u occurrence()这两个search类的方法 在searcheryfield中,我在search\u revisions()中创建了一个生成器,仅在收集第一个结果后在collect\u one\u occurrence()中放弃该生成器。在SearcherCondition中,我在循环中放入了一个条件。对于

我需要搜索其他事物中第一个、最后一个、任何或所有出现的事物。为了避免重复我自己()我提出了以下解决方案

有趣的是
search\u revisions()
collect\u one\u occurrence()
这两个
search类的方法

searcheryfield
中,我在
search\u revisions()
中创建了一个生成器,仅在收集第一个结果后在
collect\u one\u occurrence()
中放弃该生成器。在
SearcherCondition
中,我在循环中放入了一个条件。对于循环的每次迭代,都必须检查此条件

我无法决定我(ab)使用收益率并随后放弃发电机是天才之举还是骇人听闻的黑客行为。你怎么认为?对于这种情况,你还有其他想法吗

#!/usr/bin/python

class Revision:
  # a revision is something like a textfile.
  # the search() method will search the textfile
  # and return the lines which match the given pattern.
  # for demonstration purposes this class is simplified
  # to return predefined results
  def __init__(self, results):
    self.results = results
  def search(self, pattern):
    return self.results

class AbstractSearcher:
  def __init__(self, revisions):
    self.revisions = revisions
  def search_for_first_occurence(self, pattern):
    keys = sorted(self.revisions.iterkeys())
    return self.collect_one_occurence(keys, pattern)
  def search_for_last_occurence(self, pattern):
    keys = sorted(self.revisions.iterkeys(), reverse = True)
    return self.collect_one_occurence(keys, pattern)
  def search_for_any_occurence(self, pattern):
    keys = self.revisions.iterkeys()
    return self.collect_one_occurence(keys, pattern)
  def search_for_all_occurences(self, pattern):
    keys = self.revisions.iterkeys()
    return self.collect_all_occurences(keys, pattern)

class SearcherYield(AbstractSearcher):

  def search_revisions(self, keys, pattern):
    # create generator which yields the results one by one
    for key in keys:
      rev = self.revisions[key]
      result = rev.search(pattern)
      if result:
        yield result

  def collect_one_occurence(self, keys, pattern):
    # take the first result and then abandon the generator
    for result in self.search_revisions(keys, pattern):
      return result
    return []

  def collect_all_occurences(self, keys, pattern):
    # collect all results from generator
    results = []
    for result in self.search_revisions(keys, pattern):
      results.extend(result)
    return results

class SearcherCondition(AbstractSearcher):

  def search_revisions(self, keys, pattern, just_one):
    # collect either all results from all revisions
    # or break the loop after first result found
    results = []
    for key in keys:
      rev = self.revisions[key]
      result = rev.search(pattern)
      if result:
        results.extend(result)
        if just_one:
          break
    return results

  def collect_one_occurence(self, keys, pattern):
    return self.search_revisions(keys, pattern, just_one = True)

  def collect_all_occurences(self, keys, pattern):
    return self.search_revisions(keys, pattern, just_one = False)

def demo(searcher):
  print searcher.__class__.__name__
  print 'first:', searcher.search_for_first_occurence('foo')
  print 'last: ', searcher.search_for_last_occurence('foo')
  print 'any:  ', searcher.search_for_any_occurence('foo')
  print 'all:  ', searcher.search_for_all_occurences('foo')

def main():
  revisions = {
        1: Revision([]),
        2: Revision(['a', 'b']),
        3: Revision(['c']),
        4: Revision(['d','e', 'f']),
        5: Revision([])}
  demo(SearcherYield(revisions))
  demo(SearcherCondition(revisions))

if __name__ == '__main__':
  main()
一些上下文:修订基本上是文本文件。你可以把它们想象成维基页面的修订版。通常有成百上千的修订。每个版本包含多达数千行的文本。也有一些情况下,只有几个版本,每个版本只有几行

在修订中搜索将在文本中搜索模式并返回匹配行。有时有成千上万的结果,有时没有结果


有时我只需要知道在任何修订中是否有任何结果(搜索任何结果)。有时我不得不收集所有的结果以便进一步处理(搜索全部)。有时我只需要匹配的第一个版本,有时只需要最后一个版本(搜索第一个和最后一个)。

如果查找项是不可变的,并且集合是任何有序集合,这将解决您的问题:

positions=[i代表i,如果项==lookup\u项,则枚举(collec)中的项]


它实际上会返回collec中出现查找项的所有位置。

我做了一个基准测试。结果如下:

$ ./benchmark.py 
benchmark with revcount: 1000 timeitcount: 1000
last, first, yield: 0.902059793472
last, first,  cond: 0.897155046463
last,   all, yield: 0.818709135056
last,   all,  cond: 0.818334102631
 all,   all, yield: 1.26602506638
 all,   all,  cond: 1.17208003998
benchmark with revcount: 2000 timeitcount: 1000
last, first, yield: 1.80768609047
last, first,  cond: 1.84234118462
last,   all, yield: 1.64661192894
last,   all,  cond: 1.67588806152
 all,   all, yield: 2.55621600151
 all,   all,  cond: 2.37582707405
benchmark with revcount: 10000 timeitcount: 1000
last, first, yield: 9.34304785728
last, first,  cond: 9.33725094795
last,   all, yield: 8.4673140049
last,   all,  cond: 8.49153590202
 all,   all, yield: 12.9636368752
 all,   all,  cond: 11.780673027
产率和条件解显示出非常相似的时间。我认为这是因为生成器(yield)有一个循环,其中包含一个条件(如果不是空的或类似的)。我以为我避开了循环中的情况,但我只是把它移到了视线之外

无论如何,数字表明性能基本相同,因此应该根据可读性来判断代码。我将坚持循环中的条件。我喜欢直白

以下是基准代码:

#!/usr/bin/python

import functools
import timeit

class Revision:
  # a revision is something like a textfile.
  # the search() method will search the textfile
  # and return the lines which match the given pattern.
  # for demonstration purposes this class is simplified
  # to return predefined results
  def __init__(self, results):
    self.results = results
  def search(self, pattern):
    return self.results

class AbstractSearcher:
  def __init__(self, revisions):
    self.revisions = revisions
  def search_for_first_occurence(self, pattern):
    keys = sorted(self.revisions.iterkeys())
    return self.collect_one_occurence(keys, pattern)
  def search_for_last_occurence(self, pattern):
    keys = sorted(self.revisions.iterkeys(), reverse = True)
    return self.collect_one_occurence(keys, pattern)
  def search_for_any_occurence(self, pattern):
    keys = self.revisions.iterkeys()
    return self.collect_one_occurence(keys, pattern)
  def search_for_all_occurences(self, pattern):
    keys = self.revisions.iterkeys()
    return self.collect_all_occurences(keys, pattern)

class SearcherYield(AbstractSearcher):

  def search_revisions(self, keys, pattern):
    # create generator which yields the results one by one
    for key in keys:
      rev = self.revisions[key]
      result = rev.search(pattern)
      if result:
        yield result

  def collect_one_occurence(self, keys, pattern):
    # take the first result and then abandon the generator
    for result in self.search_revisions(keys, pattern):
      return result
    return []

  def collect_all_occurences(self, keys, pattern):
    # collect all results from generator
    results = []
    for result in self.search_revisions(keys, pattern):
      results.extend(result)
    return results

class SearcherCondition(AbstractSearcher):

  def search_revisions(self, keys, pattern, just_one):
    # collect either all results from all revisions
    # or break the loop after first result found
    results = []
    for key in keys:
      rev = self.revisions[key]
      result = rev.search(pattern)
      if result:
        results.extend(result)
        if just_one:
          break
    return results

  def collect_one_occurence(self, keys, pattern):
    return self.search_revisions(keys, pattern, just_one = True)

  def collect_all_occurences(self, keys, pattern):
    return self.search_revisions(keys, pattern, just_one = False)

def benchmark(revcount, timeitcount):

  lastrev = {}
  for i in range(revcount):
    lastrev[i] = Revision([])
  lastrev[revcount] = Revision([1])

  allrevs = {}
  for i in range(revcount):
    allrevs[i] = Revision([1])

  last_yield = SearcherYield(lastrev)
  last_cond = SearcherCondition(lastrev)
  all_yield = SearcherYield(allrevs)
  all_cond = SearcherCondition(allrevs)

  lfy = functools.partial(last_yield.search_for_first_occurence, 'foo')
  lfc = functools.partial(last_cond.search_for_first_occurence, 'foo')
  lay = functools.partial(last_yield.search_for_all_occurences, 'foo')
  lac = functools.partial(last_cond.search_for_all_occurences, 'foo')
  aay = functools.partial(all_yield.search_for_all_occurences, 'foo')
  aac = functools.partial(all_cond.search_for_all_occurences, 'foo')

  print 'benchmark with revcount: %d timeitcount: %d' % (revcount, timeitcount)
  print 'last, first, yield:', timeit.timeit(lfy, number = timeitcount)
  print 'last, first,  cond:', timeit.timeit(lfc, number = timeitcount)
  print 'last,   all, yield:', timeit.timeit(lay, number = timeitcount)
  print 'last,   all,  cond:', timeit.timeit(lac, number = timeitcount)
  print ' all,   all, yield:', timeit.timeit(aay, number = timeitcount)
  print ' all,   all,  cond:', timeit.timeit(aac, number = timeitcount)

def main():
  timeitcount = 1000
  benchmark(1000, timeitcount)
  benchmark(2000, timeitcount)
  benchmark(10000, timeitcount)

if __name__ == '__main__':
  main()
有关我的系统的一些信息:

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 10.04.1 LTS
Release:    10.04
Codename:   lucid
$ uname -a
Linux lesmana-laptop 2.6.32-26-generic #46-Ubuntu SMP Tue Oct 26 16:46:46 UTC 2010 i686 GNU/Linux
$ python --version
Python 2.6.5
$ cat /proc/cpuinfo | grep name
model name  : Intel(R) Pentium(R) M processor 1.60GHz

就我个人而言,就可读性而言,我支持收益率,但这还差一点。除了我认为它是一个伟大的代码构造并且适用于许多情况之外,我真的没有太多的理由

您可能已经知道这一点,但是代码需要将匹配的修订返回给调用方。修复代码更改最少的方法是在修订搜索方法返回结果时返回修订的链接

通过结合使用PythoniTertools模块和yield,您可能可以减少代码。可读性可以说是垃圾,但它是如此令人敬畏的极客时尚:

from itertools import chain,repeat,islice,ifilter
def collect_one_occurence(self, keys, pattern):
    return chain(ifilter(None,(rev.search(pattern) for rev in (self.revisions[key] for key in keys)),repeat([]).next()

def collect_all_occurences(self, keys, pattern):
    return list(chain(*[rev.search(pattern) for rev in (self.revisions[key] for key in keys)]))

显然,您可以扩展代码以使其更具可读性,但出于基准测试的目的,我将其折叠了。。想知道这能在多大程度上改善您当前的结果吗?

这太复杂了。不过,除非你能提供一些更有用的上下文,否则我无法告诉你如何修复它;我从您的示例中得到的只是您编写了太多的代码。你在寻找什么?你需要一个术语移植:你所调用的第一个/最后一个实际上是最小/最大键,并(实际上)执行
sorted(iterable)[0]
而不是
min(iterable)
有点让人困惑。@JohnMachin:请重新阅读代码。代码未执行排序(iterable)[0]
。匹配的第一个版本不一定是排序列表中的第一个版本。这并不能解决我的问题。我主要不需要带有匹配项的修订索引,而是需要修订中的匹配行。此外,搜索不会比较是否相等,而是搜索修订行中的模式。此外,当有数千个版本,每个版本有数千行,而我只想要第一个匹配时,在第一次点击后中止搜索循环会有很大的不同。“我需要搜索其他内容中出现的第一个、最后一个、任何或所有内容。”-我想你没有很清楚地说明你的问题