“为”循环第一次迭代

问候pyc-sires和py-ladies,我想问一下在第一次循环迭代中是否有一个优雅的pythonic方法来执行一些函数。 我能想到的唯一可能性是:

first = True for member in something.get(): if first: root.copy(member) first = False else: somewhereElse.copy(member) foo(member) 

头尾devise模式有几种select。

 seq= something.get() root.copy( seq[0] ) foo( seq[0] ) for member in seq[1:]: somewhereElse.copy(member) foo( member ) 

或这个

 seq_iter= iter( something.get() ) head = seq_iter.next() root.copy( head ) foo( head ) for member in seq_iter: somewhereElse.copy( member ) foo( member ) 

人们呜呜,这是不是“干”,因为“冗余foo(成员)”的代码。 这是一个荒谬的说法。 如果这是真的,那么所有的function只能使用一次。 如果只能有一个参考,定义一个函数有什么意义?

像这样的东西应该工作。

 for i, member in enumerate(something.get()): if i == 0: # Do thing # Code for everything 

不过,我强烈build议考虑一下你的代码,看看你是否真的需要这样做,因为它有点“脏”。 最好是先取得需要特殊处理的元素,然后对循环中的所有其他元素进行定期处理。

我可以看到不这样做的唯一原因是你要从一个生成器expression式获得一个很大的列表(因为它不适合内存),或者类似的情况。

怎么样:

 my_array = something.get() for member in my_array: if my_array.index(member) == 0: root.copy(member) else: somewhereElse.copy(member) foo(member) 

或者可能:

 for index, member in enumerate(something.get()): if index == 0: root.copy(member) else: somewhereElse.copy(member) foo(member) 

索引方法的文档。

我认为这是相当优雅的,但也许过于复杂

 from itertools import chain, repeat, izip for place, member in izip(chain([root], repeat(somewhereElse)), something.get()): place.copy(member) foo(member) 

这工作:

 for number, member in enumerate(something.get()): if not number: root.copy(member) else: somewhereElse.copy(member) foo(member) 

不过,在大多数情况下,我build议只是遍历whatever[1:]并在循环之外做根本事情; 这通常更具可读性。 当然取决于你的用例。

如果something.get()迭代一些东西,你也可以这样做:

 root.copy(something.get()) for member in something.get(): # the rest of the loop 

在这里,我可以拿出一个可以看起来很“丑陋”的Pythonic成语。 尽pipe很可能我会用你提出的问题来提问,只是为了让代码保持更明显,尽pipe不那么优雅。

 def copy_iter(): yield root.copy while True: yield somewhereElse.copy for member, copy in zip(something.get(), copy_iter()): copy(member) foo(member) 

(对不起 – 我发布的第一张,在编辑之前,表格不起作用,我忘了实际上得到一个“复制”对象的迭代器)

如何使用它,并消耗第一个元素?

编辑:回到OP的问题,有一个常见的操作,你想要在所有的元素上执行,然后一个操作,你要执行的第一个元素,另一个rest。

如果它只是一个单一的函数调用,我会说只是写了两次。 它不会结束世界。 如果涉及更多,则可以使用装饰器将常用操作包装为“第一个”function和“rest”function。

 def common(item): print "common (x**2):", item**2 def wrap_common(func): """Wraps `func` with a common operation""" def wrapped(item): func(item) common(item) return wrapped @wrap_common def first(item): """Performed on first item""" print "first:", item+2 @wrap_common def rest(item): """Performed on rest of items""" print "rest:", item+5 items = iter(range(5)) first(items.next()) for item in items: rest(item) 

输出:

 first: 2 common (x**2): 0 rest: 6 common (x**2): 1 rest: 7 common (x**2): 4 rest: 8 common (x**2): 9 rest: 9 common (x**2): 16 

或者你可以做一个切片:

 first(items[0]) for item in items[1:]: rest(item) 

我认为第一个S.Lott解决scheme是最好的,但如果你使用了一个非常新的Python(> = 2.6,我认为,因为izip_longest在该版本之前似乎不可用),可以做另一种select,第一个元素和后续元素,并且可以很容易地修改为第一个,第二个,第三个元素执行不同的操作。

 from itertools import izip_longest seq = [1, 2, 3, 4, 5] def headfunc(value): # do something print "1st value: %s" % value def tailfunc(value): # do something else print "this is another value: %s" % value def foo(value): print "perform this at ANY iteration." for member, func in izip_longest(seq, [headfunc], fillvalue=tailfunc): func(member) foo(member) 

你不能在循环之前做root.copy(something.get())吗?

编辑:对不起,我错过了第二位。 但是你得到了一般的想法。 否则,枚举并检查0

编辑2:好吧,摆脱愚蠢的第二个想法。

我不知道Python,但我几乎使用你的例子的确切模式。
我所做的也是使if条件最频繁,所以通常检查if( first == false )
为什么? 对于长循环来说,首先只有一次是真的,其他时间将是假的,这意味着除了第一个循环之外,程序将检查条件并跳转到else部分。
首先检查是否为false,只有一个跳转到else部分。 我真的不知道这样做是否能够提高效率,但我仍然这样做,只是为了和我内心的书呆子保持和平。

PS:是的,我知道在进入if部分时,也必须跳过else才能继续执行,所以可能我的做法是无用的,但感觉很好。 :d

你的问题是矛盾的。 你说“只在第一次迭代时做一些事情”,实际上你说的是在第一次/后续迭代中做一些不同的事情。 这是我如何尝试它:

 copyfn = root.copy for member in something.get(): copyfn(member) foo(member) copyfn = somewhereElse.copy