我想问一下,是否有一种优雅的Pythonic方法,在第一次循环迭代时执行某个函数。 我能想到的唯一可能性是:
first = True
for member in something.get():
if first:
root.copy(member)
first = False
else:
somewhereElse.copy(member)
foo(member)
类似这样的代码应该可以运行。
for i, member in enumerate(something.get()):
if i == 0:
# Do thing
# Code for everything
不过,我强烈建议您思考一下自己的代码,看看是否真的需要这样做,因为这种方式有点“肮脏”。更好的方法是提前获取需要特殊处理的元素,然后在循环中为所有其他元素执行常规处理。
我能想到不这样做的唯一原因是对于从生成器表达式获取的大列表(您不希望提前获取它,因为它无法适应内存)或类似情况。
在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 )
有些人抱怨这段代码中的"redundant foo(member)"不符合"DRY"原则,这是一个荒谬的说法。如果这是真的,那么所有函数都只能使用一次。定义函数的意义在于可以有多个引用,否则就没有意义了。
member
,从而污染它。 - Skilldrickmember
,污染了命名空间并赋予了两个不同的含义。其中一个member
是头部;另一个members
则是尾部。但它们都是成员。我不确定你想表达什么意思。 - S.Lottfoo
是如何违反DRY原则的。这是一个被重复使用的函数。这不是函数定义的意义所在吗? - S.Lott这样怎么样:
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 的文档。
if member is my_array[0]
,那么代码会更加安全易读。但是,如果 my_array 在索引 0 和另一个索引处引用了同一个对象,这种方法仍然无法正常工作。 - Joooeey这是可行的:
for number, member in enumerate(something.get()):
if not number:
root.copy(member)
else:
somewhereElse.copy(member)
foo(member)
whatever[1:]
并在循环外执行根操作;当然这取决于你的使用场景,但通常更易读。not something
检测零的情况,具体要看上下文。有些代码/数据场景非常混乱,最好使用 not
来消除杂散的 None
甚至是 ""
如果代码很糟糕,这些都是可能的话。在其他情况下,你明确地 不 想检测到其他类似的假值。在这里,你正在处理从 enumerate
生成的数字,所以为了绝对清晰,number == 0
我认为会更好一些,但使用 not
绝不会 "极其难懂"!哈哈,如果你编写 Python,而你不知道什么是假值,那你就麻烦了。 - NeilG在这里,我可以用一种Python惯用语,看起来很“漂亮”。虽然,最有可能我会使用你在提问中建议的形式,只是为了让代码更明显,尽管不太优雅。
def copy_iter():
yield root.copy
while True:
yield somewhereElse.copy
for member, copy in zip(something.get(), copy_iter()):
copy(member)
foo(member)
很抱歉 - 我在编辑之前发布的第一篇文章无法工作,我忘记为“copy”对象实际获取迭代器了。
from itertools import chain, repeat, izip
for place, member in izip(chain([root], repeat(somewhereElse)), something.get()):
place.copy(member)
foo(member)
如果 something.get() 遍历了 something,你也可以按照以下方式完成:
root.copy(something.get())
for member in something.get():
# the rest of the loop
使用iter
,并消耗第一个元素如何?
编辑:回到问题,有一个常见的操作是要对所有元素执行,然后对第一个元素执行另一个操作,对其余元素执行另一个操作。
如果只是单个函数调用,我建议写两次。这不会毁掉世界。如果涉及更多操作,您可以使用装饰器将“第一个”函数和“其余”函数与一个公共操作包装起来。
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的第一种解决方案是最好的选择,但如果你使用的是相当新的Python(>= 2.6,因为在该版本之前似乎没有izip_longest可用),那么还有另一个选择,可以让你针对第一个元素和后续元素执行不同的操作,并且可以很容易地修改以执行不同的操作,如第1个、第2个、第3个元素等。
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:好的,放弃了愚蠢的第二个想法。
for i, a, b, c, in os.walk(input_dir):
?这会导致ValueError: need more than 3 values to unpack
。 - bmikolajfor i,tuple in enumerate(os.walk(...)): a, b, c = tuple
- Daniel Bruce