2009-08-14 1 views
33

Эта проблема возникает у меня какое-то время. Есть ли более простой способ писать вложенные петли for в python? Например, если мой код выглядит примерно так:В python есть более простой способ написать 6 вложенных циклов?

for y in range(3): 
    for x in range(3): 
     do_something() 
     for y1 in range(3): 
     for x1 in range(3): 
      do_something_else() 

будет ли более простой способ сделать это? Я знаю, что этот код работает, но когда вы отступаете вместо использования 2 пробелов, таких как я, это может стать проблемой.

О, в примере было всего 4 вложенных петли for, чтобы упростить задачу.

+0

[См. Также.] (Http://stackoverflow.com/a/533917/1430829) –

ответ

53

Если вы часто повторяя декартово произведение, как в вашем примере, вы можете изучить Python 2.6's itertools.product - или написать свой собственный, если вы находитесь в более раннем Python.

from itertools import product 
for y, x in product(range(3), repeat=2): 
    do_something() 
    for y1, x1 in product(range(3), repeat=2): 
    do_something_else() 
+0

Если вложенный цикл выглядит так: ' '' для y в диапазоне (3): do_something1() для x в диапазоне (3): do_something() '' 'как я могу использовать продукт –

+0

@ ErtuğrulAltınboğa Без отступов невозможно сказать, что вы пытаетесь сделать Я предлагаю задать новый вопрос StackOverflow, а не пытаться получить помощь в комментариях. –

9

Когда вы сталкиваетесь с такой программной логикой, я бы, вероятно, разбил последовательность циклов на две или более отдельных функций.

Другая техника в Python заключается в использовании list comprehensions, где это возможно, вместо цикла.

2

Мой личный аргумент будет то, что вы, вероятно, делать что-то неправильно, если у вас есть 6 вложенных циклов ...

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

+2

Вот прецедент, в котором у меня в прошлом было много вложенных циклов: Представьте применение 3D-гауссовского фильтра «размытия» к 3D набор данных; у вас есть сводки во всех трех направлениях для фильтра (3 петли) и повторение каждой точки в трех направлениях, чтобы применить ее (еще три петли). Ох, и данные, которые я фильтровал, были скоростями, поэтому было три компонента для итерации (1 конечный цикл). –

6

Предположим, что каждый цикл имеет какое-то самостоятельное значение, разбить их на именованные функции:

def do_tigers(): 
    for x in range(3): 
     print something 

def do_lions(): 
    do_lionesses() 
    for x in range(3): 
     do_tigers() 

def do_penguins(): 
    for x in range(3): 
     do_lions() 

..etc. 

я мог бы, возможно, выбрали лучшие имена. 8-)

+3

Aww, no kittens :( –

+4

Я бы сделал львиц, если бы я был львом. – Skurmedel

+3

Ну, это один из способов получить котят ... Хотя, немного больше, чем я имел в виду. –

1

Таким образом, выглядит довольно простым и легким. Вы говорите, что хотите обобщить на несколько слоев циклов .... можете ли вы привести пример в реальной жизни?

Другой вариант я мог думать о том, чтобы использовать функцию для формирования параметров, а затем просто применить их в цикле

def generate_params(n): 
    return itertools.product(range(n), range(n)) 

for x,y in generate_params(3): 
    do_something() 
4

Технически, вы могли бы использовать itertools.product получить декартово произведение N последовательностей и перебирать, что:

for y, x, y1, x1 in itertools.product(range(3), repeat=4): 
    do_something_else() 

Но я не думаю, что на самом деле выигрывает вам что-нибудь читаемость-накрест.

13

Это довольно часто встречается при переходе по многомерным пространствам.Мое решение:

xy_grid = [(x, y) for x in range(3) for y in range(3)] 

for x, y in xy_grid: 
    # do something 
    for x1, y1 in xy_grid: 
     # do something else 
+0

Кроме того, если у вас есть большое количество элементов для прокрутки, возможно, лучше использовать выражение генератора, то есть 'xy_grid = ((x, y) для x в диапазоне (30000) для y в диапазоне (30000)). – Garrett

2

Из вашего кода он выглядит, как вы хотите, чтобы выполнить операцию с каждой возможной парой точек, где х и у находятся в диапазоне 0..2.

Чтобы сделать это:

for x1,y1,x2,y2 in itertools.product(range(3), repeat=4): 
    do_something_with_two_points(x1,y1,2,y2) 

Операция do_something_with_two_points будет называться 81 раз - один раз для каждой возможной комбинации точек.

3

Итераторы и генераторы Python, в частности, существуют, чтобы обеспечить хороший рефакторинг в противном случае сложных циклов. Конечно, трудно получить абстракцию из простого примера, но предполагая, что параметр 3 должен быть параметром (может быть, весь range(3) должен быть?), А две функции, которые вы вызываете, нуждаются в некоторых параметрах, которые являются переменными цикла, вы могли бы реорганизовать код:

for y in range(3): 
    for x in range(3): 
     do_something(x, y) 
     for y1 in range(3): 
     for x1 in range(3): 
      do_something_else(x, y, x1, y1) 

в, например:

def nestloop(n, *funcs): 
    head = funcs[0] 
    tail = funcs[1:] 
    for y in range(n): 
    for x in range(n): 
     yield head, x, y 
     if tail: 
     for subtup in nestloop(n, *tail): 
      yield subtup[:1] + (x, y) + subtup[1:] 

for funcandargs in nestloop(3, do_something, do_something_else): 
    funcandargs[0](*funcandargs[1:]) 

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

 Смежные вопросы

  • Нет связанных вопросов^_^