"for" 循环第一次迭代
Posted
技术标签:
【中文标题】"for" 循环第一次迭代【英文标题】:"For" loop first iteration 【发布时间】:2010-12-28 00:08:02 【问题描述】:我想询问是否有一种优雅的 Python 方式可以在第一次循环迭代时执行某些函数。 我能想到的唯一可能是:
first = True
for member in something.get():
if first:
root.copy(member)
first = False
else:
somewhereElse.copy(member)
foo(member)
【问题讨论】:
【参考方案1】:这对我有用
dup_count = 0
for x in reversed(dup_list):
dup_count += 1
if dup_count == 1:
print("First obj : ".format(dup_count,x))
else:
print("Object # : ".format( dup_count,x ))
【讨论】:
【参考方案2】:你的问题是矛盾的。您说“仅在第一次迭代中做某事”,而实际上您是在说在第一次/后续迭代中做一些不同的事情。这就是我尝试的方式:
copyfn = root.copy
for member in something.get():
copyfn(member)
foo(member)
copyfn = somewhereElse.copy
【讨论】:
【参考方案3】:使用iter
,并消费第一个元素怎么样?
编辑: 回到 OP 的问题,您要对所有元素执行一个通用操作,然后您要对第一个元素执行一个操作,然后在休息。
如果只是一个函数调用,我会说只写两次。它不会终结世界。如果涉及更多,您可以使用装饰器将您的“first”函数和“rest”函数包装为一个通用操作。
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)
【讨论】:
【参考方案4】:怎么样:
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)
index-method 的文档。
【讨论】:
如果 my_array 有其他成员比较等于第一个成员,第一个选项不起作用。 写if member is my_array[0]
会更省钱,更容易阅读。但是如果 my_array 在索引 0 和另一个索引处引用同一个对象,这仍然不起作用。【参考方案5】:
我认为第一个 S.Lott 解决方案是最好的,但是如果您使用的是最近的 python(我认为 >= 2.6,因为 izip_longest 在该版本之前似乎不可用),那么还有另一个选择可以让您做不同的事情第一个元素和后续元素的东西,并且可以轻松修改以对第一个、第二个、第三个元素执行不同的操作......也是如此。
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)
【讨论】:
这可能很聪明,但远非清晰或直观。【参考方案6】:我不了解 Python,但我使用的模式几乎与您的示例完全相同。
我所做的也是使if
条件最频繁,所以通常检查if( first == false )
为什么?对于长循环,first 仅一次为真,其他时间均为假,这意味着在除第一个循环之外的所有循环中,程序将检查条件并跳转到 else 部分。
通过检查 first 是否为 false,将只有一次跳转到 else 部分。我真的不知道这是否会提高效率,但无论如何我都会这样做,只是为了与我内心的书呆子和平相处。
PS:是的,我知道进入if部分的时候,还要跳过else才能继续执行,所以可能我的做法没用,但是感觉不错。 :D
【讨论】:
【参考方案7】:Head-Tail 设计模式有多种选择。
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 )
人们抱怨这不是“DRY”,因为“冗余 foo(member)”代码。这是一个荒谬的说法。如果这是真的,那么所有功能只能使用一次。如果你只能有一个引用,那么定义一个函数有什么意义呢?
【讨论】:
你这样用额外的member
污染了命名空间。
从技术上讲,它并不是 DRY,因为您正在复制处理两个地方的任何成员的语义,但由于代码如此短且紧密,我认为这一点没有实际意义。但是,如果两者之间有更多代码,或者如果将两者抽象成单独的函数,那么我会正确地指出它违反了 DRY 原则。
@Skilldrick:示例代码还污染了命名空间,对一个名称member
具有两种不同的含义。一个member
是负责人;另一个members
的尾巴。然而,他们都是成员。我不确定你在说什么。
@Daniel Bruce:我看不出在两个(或更多)地方使用foo
是如何违反 DRY 的。这是一个可重用的功能。这不是函数定义的重点吗?
DRY 与否,只要您重复的代码不超过一两行,这是最容易阅读的,并且胜过我书中的其他问题。如果序列很长并且您不想创建 all-elements-but-one 的临时副本,则可以使用 itertools.islice。【参考方案8】:
我认为这很优雅,但它的作用可能过于复杂......
from itertools import chain, repeat, izip
for place, member in izip(chain([root], repeat(somewhereElse)), something.get()):
place.copy(member)
foo(member)
【讨论】:
【参考方案9】:在这里,我可以提出一个看起来“优雅”的 Pythonic 习语。虽然,我很可能会使用您在提出问题时建议的形式,只是为了让代码更明显,但不那么优雅。
def copy_iter():
yield root.copy
while True:
yield somewhereElse.copy
for member, copy in zip(something.get(), copy_iter()):
copy(member)
foo(member)
(抱歉 - 我第一次发布,在编辑之前,表单不起作用,我忘记实际获取“复制”对象的迭代器)
【讨论】:
嘿嘿,在看到你的解决方案之前,我想出了相同的解决方案,但使用的是 itertools :-)【参考方案10】:你不能在循环之前做root.copy(something.get())
吗?
编辑:对不起,我错过了第二点。但你得到了一般的想法。否则,枚举并检查0
?
EDIT2:好的,摆脱愚蠢的第二个想法。
【讨论】:
【参考方案11】:如果 something.get() 迭代了某些东西,你也可以这样做:
root.copy(something.get())
for member in something.get():
# the rest of the loop
【讨论】:
【参考方案12】:这行得通:
for number, member in enumerate(something.get()):
if not number:
root.copy(member)
else:
somewhereElse.copy(member)
foo(member)
不过,在大多数情况下,我建议只遍历 whatever[1:]
并在循环外执行根操作;这通常更具可读性。当然,这取决于您的用例。
【讨论】:
-1:“不是数字”?这真的非常晦涩难懂。数字 == 0 有什么问题? 我会使用 number == 0 因为这与您感兴趣的语义相匹配(可迭代的第一项,索引为 0)。然而,这种语法远非“非常晦涩难懂”,尽管对于测试序列空虚性更有用。 然后将其设为 number == 0,如果这看起来更清楚的话。 0 是唯一评估为 False 的数字。【参考方案13】:这样的事情应该可以工作。
for i, member in enumerate(something.get()):
if i == 0:
# Do thing
# Code for everything
但是,我强烈建议您考虑一下您的代码,看看您是否真的必须这样做,因为它有点“脏”。最好先获取需要特殊处理的元素,然后对循环中的所有其他元素进行常规处理。
我可以看到不这样做的唯一原因是您将从生成器表达式中获得一个大列表(您不想预先获取它,因为它不适合内存),或类似情况。
【讨论】:
其实你不需要一个大的列表。如果 something.get() 返回一个生成器(而不是一个列表),那么你就是黄金。 这对于有元组的循环如何工作? IE。for i, a, b, c, in os.walk(input_dir):
?这给了ValueError: need more than 3 values to unpack
。
您的示例代码有多个错误:for 表达式中的尾随逗号,并且没有调用 enumerate()。您必须手动解压缩 for 循环中的元组:python for i,tuple in enumerate(os.walk(...)): a, b, c = tuple
但这会检查条件的次数与容器中的元素一样多...以上是关于"for" 循环第一次迭代的主要内容,如果未能解决你的问题,请参考以下文章
for 循环的“计数限制”表达式是不是只计算一次,还是在每次迭代时计算?