“Для” цикла первое повторение

Я хотел бы запросить, существует ли изящный pythonic способ выполнить некоторую функцию на первом повторении цикла. Единственная возможность, о которой я могу думать:

first = True
for member in something.get():
    if first:
        root.copy(member)
        first = False
    else:
        somewhereElse.copy(member)
    foo(member)
65
задан Rince 21 March 2019 в 10:37
поделиться

11 ответов

У вас есть несколько вариантов для шаблона проектирования Голова-Хвост .

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 (member) "код. Это нелепое заявление. Если бы это было так, то все функции можно было бы использовать только один раз. Какой смысл определять функцию, если у вас может быть только одна ссылка?

40
ответ дан 24 November 2019 в 15:21
поделиться

Здесь я мог бы использовать идиому Pythonic, которая может выглядеть "pertty". Хотя, скорее всего, я бы использовал форму, которую вы предложили, задавая вопрос, просто для того, чтобы код оставался более очевидным, хотя и менее элегантным.

def copy_iter():
    yield root.copy
    while True:
        yield somewhereElse.copy

for member, copy in zip(something.get(), copy_iter()):
    copy(member)
    foo(member)

(извините, первая, которую я опубликовал, перед редактированием, форма не будет работать, я забыл получить итератор для объекта «копия»)

5
ответ дан 24 November 2019 в 15:21
поделиться

как насчет:

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 .

11
ответ дан 24 November 2019 в 15:21
поделиться

Что-то вроде этого должно сработать.

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

Однако я настоятельно рекомендую подумать о вашем коде, чтобы увидеть, действительно ли вам нужно делать это таким образом, потому что это своего рода "грязный". Лучше было бы заранее получить элемент, требующий особой обработки, а затем выполнять регулярную обработку для всех остальных в цикле.

Единственная причина, по которой я мог не делать этого таким образом, - это большой список, в котором вы будете получение из выражения генератора (которое вы не хотели бы получать заранее, потому что оно не помещается в памяти) или аналогичные ситуации.

61
ответ дан 24 November 2019 в 15:21
поделиться

Я думаю, это довольно элегантно, но, возможно, слишком запутано для того, что оно делает ...

from itertools import chain, repeat, izip
for place, member in izip(chain([root], repeat(somewhereElse)), something.get()):
    place.copy(member)
    foo(member)
6
ответ дан 24 November 2019 в 15:21
поделиться

Разве вы не можете выполнить root.copy (something.get ()) перед циклом?

РЕДАКТИРОВАТЬ: Извините, я пропустил второй бит . Но вы поняли общую идею. В противном случае выполните перечисление и проверьте наличие 0 ?

EDIT2: Хорошо, избавился от второй глупой идеи.

1
ответ дан 24 November 2019 в 15:21
поделиться

Если something.get () выполняет итерацию над чем-то, вы также можете сделать это следующим образом:

root.copy(something.get())

for member in something.get():
  #  the rest of the loop
3
ответ дан 24 November 2019 в 15:21
поделиться

Это работает:

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

В большинстве случаев, однако, я предлагаю просто перебирать что угодно [1:] и выполнять корневую операцию вне цикла; это обычно более читабельно. Конечно, это зависит от вашего варианта использования.

5
ответ дан 24 November 2019 в 15:21
поделиться

Я не знаю Python, но я использую почти точный шаблон из вашего примера.
Я также делаю условие if наиболее частым, поэтому обычно проверяю if (first == false)
Почему? для длинных циклов first будет истинным только один раз и будет ложным все остальные, что означает, что во всех циклах, кроме первого, программа проверит условие и перейдет к части else.
При проверке на ложность первого будет только один переход к другой части. Я действительно не знаю, добавляет ли это вообще эффективности, но я все равно делаю это, просто чтобы быть в мире со своим внутренним ботаником.

PS: Да, я знаю, что при входе в часть if он также должен перепрыгивать через else, чтобы продолжить выполнение, поэтому, вероятно, мой способ сделать это бесполезен, но это приятно. : D

1
ответ дан 24 November 2019 в 15:21
поделиться

Как насчет использования iter и потребления первого элемента?

Изменить: Возвращаясь к вопросу OP, там - это обычная операция, которую вы хотите выполнить со всеми элементами, а затем одну операцию, которую вы хотите выполнить с первым элементом, а другую - с остальными.

Если это всего лишь вызов одной функции, я бы сказал, просто напишите ее дважды. Это не конец света. Если это более сложно, вы можете использовать декоратор, чтобы обернуть свою "первую" функцию и "отдых" функция с общей операцией.

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)
3
ответ дан 24 November 2019 в 15:21
поделиться

Я думаю, что первое решение 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)
3
ответ дан 24 November 2019 в 15:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: