Емуляція циклу виконання часу в Python?


797

Мені потрібно емулювати цикл виконання часу в програмі Python. На жаль, наступний прямий код не працює:

list_of_ints = [ 1, 2, 3 ]
iterator = list_of_ints.__iter__()
element = None

while True:
  if element:
    print element

  try:
    element = iterator.next()
  except StopIteration:
    break

print "done"

Замість "1,2,3, зроблено", він друкує такий вихід:

[stdout:]1
[stdout:]2
[stdout:]3
None['Traceback (most recent call last):
', '  File "test_python.py", line 8, in <module>
    s = i.next()
', 'StopIteration
']

Що я можу зробити, щоб знайти виняток "зупинити ітерацію" та правильно перервати цикл?

Приклад того, чому може знадобитися таке, показаний нижче як псевдокод.

Державна машина:

s = ""
while True :
  if state is STATE_CODE :
    if "//" in s :
      tokens.add( TOKEN_COMMENT, s.split( "//" )[1] )
      state = STATE_COMMENT
    else :
      tokens.add( TOKEN_CODE, s )
  if state is STATE_COMMENT :
    if "//" in s :
      tokens.append( TOKEN_COMMENT, s.split( "//" )[1] )
    else
      state = STATE_CODE
      # Re-evaluate same line
      continue
  try :
    s = i.next()
  except StopIteration :
    break

4
Гм ... Це не належне "до-час"; це просто "робити назавжди". Що не так із "істинним" та "зламаним"?
S.Lott

70
С. Лотт: Я впевнений, що його питання стосувалося того, як реалізовувати робити під час роботи в python. Тож я б не очікував, що його код буде абсолютно правильним. Крім того, він дуже близький до справи, поки ... він перевіряє стан в кінці циклу "назавжди", щоб побачити, чи повинен він вирватися. Це не "робити-назавжди".
Том

4
так що ... ваш початковий приклад код насправді працює для мене без проблем, і я не отримую цього прослідку. це правильна ідіома для циклу "do while", де умовою перерви є виснаження ітератора. як правило, ви б встановили s=i.next()замість None і, можливо, виконали якусь початкову роботу, а не просто зробили ваш перший прохід через цикл марним, хоча.
недоріг

3
@underrun На жаль, у публікації не позначено, яка версія Python використовувалася - оригінальний фрагмент також працює для мене, використовуючи 2.7, імовірно, через оновлення самої мови Python.
Ганнеле

Відповіді:


984

Я не впевнений, що ти намагаєшся зробити. Ви можете реалізувати цикл "час виконання" таким чином:

while True:
  stuff()
  if fail_condition:
    break

Або:

stuff()
while not fail_condition:
  stuff()

Що ви робите, намагаючись використати цикл do while для друку матеріалів у списку? Чому б просто не використовувати:

for i in l:
  print i
print "done"

Оновлення:

Отже, у вас є список рядків? І ви хочете продовжувати це повторювати? Як щодо:

for s in l: 
  while True: 
    stuff() 
    # use a "break" instead of s = i.next()

Це здається чимось близьким до того, що ви хотіли б? З прикладом коду, це:

for s in some_list:
  while True:
    if state is STATE_CODE:
      if "//" in s:
        tokens.add( TOKEN_COMMENT, s.split( "//" )[1] )
        state = STATE_COMMENT
      else :
        tokens.add( TOKEN_CODE, s )
    if state is STATE_COMMENT:
      if "//" in s:
        tokens.append( TOKEN_COMMENT, s.split( "//" )[1] )
        break # get next s
      else:
        state = STATE_CODE
        # re-evaluate same line
        # continues automatically

1
мені потрібно створити державну машину. У державній машині це звичайний випадок, щоб переоцінити CURRENT оператор, тому мені потрібно «продовжити», не повторюючи наступний елемент. Я не знаю, як зробити таку річ у 'for s in l:' iteration :(. У циклі виконання часу 'продовження' буде переоцінено поточний елемент, ітерація в кінці
grigoryvp

Ви маєте на увазі, що вам потрібно слідкувати за своїм місцем у списку? Таким чином, коли ви повернетеся в той самий стан, ви зможете вибрати там, де ви зупинилися? Дайте трохи більше контексту. Здається, вам може бути краще використовувати індекс в списку.
Том

Дякую, я прокоментував ваш псевдокод ... ваш приклад здається поганим, оскільки ви, здається, обробляєте "//" так само, незалежно від того, в якому стані ви перебуваєте. Також це справжній код, де ви обробляєте коментарі? Що робити, якщо у вас є рядки з косою рисою? тобто: print "blah // <- це тебе збентежує?"
Том

4
Прикро, що пітон не має циклу очікування. Пітон сухий, так?
Kr0e

43
Також дивіться PEP 315 щодо офіційної позиції / обґрунтування: "Користувачам мови рекомендується використовувати форму while-True з внутрішнім if-break, коли цикл виконання часу був би відповідним."
dtk

310

Ось дуже простий спосіб емуляції циклу виконання часу:

condition = True
while condition:
    # loop body here
    condition = test_loop_condition()
# end of loop

Ключові особливості циклу виконання часу - те, що тіло циклу завжди виконується принаймні один раз і що умова оцінюється внизу тіла циклу. Структура управління, показана тут, виконує обидва ці пункти, не потребуючи винятків або заяв про розриви. Це вводить одну додаткову булеву змінну.


11
Це не завжди додає додаткову булеву змінну. Часто вже є щось, що вже існує, стан яких можна перевірити.
мартіно

14
Причина, яка мені найбільше подобається у цьому рішенні, полягає в тому, що він не додає іншої умови, це все одно лише один цикл, і якщо ви вибрали хороше ім’я для змінної помічника, вся структура є цілком зрозумілою.
Роберто

4
ПРИМІТКА. Хоча це стосується оригінального питання, цей підхід є менш гнучким, ніж використання break. Зокрема, якщо є логіка, потрібна ПІСЛЯ test_loop_condition(), яка не повинна виконуватися після того, як ми це зробимо, вона повинна бути завершена if condition:. До речі, conditionрозпливчасто. Більш описовий: moreабо notDone.
ToolmakerSteve

7
@ToolmakerSteve Я не згоден. Я дуже рідко використовую breakцикли, і коли я стикаюся з цим кодом, який я підтримую, я виявляю, що цикл, найчастіше, міг бути написаний без нього. Представлене рішення - IMO - це найясніший спосіб представити дію, будуючи конструкцію в python.
безчувальний

1
В ідеалі умова буде названа чимось описовим, наприклад, has_no_errorsабо end_reached(у такому випадку цикл розпочнетьсяwhile not end_reached
JosiahYoder-deactive за винятком ..

74

Мій код нижче може бути корисною реалізацією, підкреслюючи головну різницю між проти як я це розумію.

Тож у цьому випадку ви завжди хоч раз проходите цикл.

first_pass = True
while first_pass or condition:
    first_pass = False
    do_stuff()

2
Правильна відповідь, я стверджую. Плюс це дозволяє уникнути злому , для безпечного використання в спробу / крім блоків.
Zv_oDD

чи уникає jit / оптимізатор повторного тестування first_pass після першого проходу? в іншому випадку це буде прикро, хоча і, мабуть, незначним питанням виступу
Markhahn

2
@markhahn це дійсно незначні , але якщо ви дбаєте про таких деталях, ви можете intervert на 2 булеві в циклі: while condition or first_pass:. Потім conditionзавжди оцінюється спочатку, а загальна first_passоцінюється лише двічі (перша та остання ітерація). Не забудьте ініціалізувати conditionперед циклом все, що завгодно.
pLOPeGG

Гм, цікаво, я насправді вибрав навпаки навмисно, щоб не потрібно ініціалізувати умову, і тому вимагати мінімальних змін у коді. Це сказало, що я бачу вашу думку
evan54

33

Виняток порушить цикл, тож ви можете також обробити його поза циклом.

try:
  while True:
    if s:
      print s
    s = i.next()
except StopIteration:   
  pass

Я думаю, що проблема з вашим кодом полягає в тому, що поведінка breakвсередині exceptне визначена. Зазвичай breakйде лише на один рівень вгору, наприклад, breakвсерединіtry йде безпосередньо до finally(якщо він існує) поза try, але не з циклу.

Пов'язаний PEP: http://www.python.org/dev/peps/pep-3136
Пов'язане запитання: Вихід із вкладених циклів


8
Хороша практика, хоча мати лише у випробувальному описі те, що ви очікуєте, щоб кинути виняток, щоб не потрапити на небажані винятки.
Паггас

7
@PiPeep: RTFM, пошук EAFP.
vartec

2
@PiPeep: немає проблем, просто пам’ятайте, що те, що є правдою для деяких мов, може бути неправдивим для інших. Python оптимізовано для інтенсивного використання винятків.
vartec

5
перерва та продовження ідеально чітко визначені в будь-якому пункті оператора спробу / за винятком / нарешті. Вони просто їх ігнорують, або вириваються або переходять до наступної ітерації вмісту, що міститься в той час, або для циклу, якщо це доречно. Як компоненти контурних конструкцій, вони стосуються лише операторів while і for, і викликають помилку синтаксису, якщо вони стикаються в операторі класу або def, перш ніж дійти до самого внутрішнього циклу. Вони ігнорують, якщо, с і намагаються висловлювати.
ncoghlan

1
.. що важливий випадок
javadba

33
do {
  stuff()
} while (condition())

->

while True:
  stuff()
  if not condition():
    break

Ви можете виконувати функцію:

def do_while(stuff, condition):
  while condition(stuff()):
    pass

Але 1) Це некрасиво. 2) Умова повинна бути функцією з одним параметром, який повинен бути заповнений начинками (це єдина причина не використовувати класичний цикл while.)


5
Писати while True: stuff(); if not condition(): breakце дуже гарна ідея. Дякую!
Noctis Skytower

2
@ZeD, чому 1) некрасиво? Цілком
гаразд

@SergeyLossev Це буде важко зрозуміти логіку програми, оскільки вона спочатку видається нескінченним циклом, якщо між вами є багато коду "речі".
exic

16

Ось божевільне рішення іншого малюнка - використання корутин. Код все ще дуже схожий, але з однією важливою різницею; взагалі немає умов виходу! Співпраця (ланцюжок спільнот дійсно) просто припиняється, коли ви перестаєте подавати її даними.

def coroutine(func):
    """Coroutine decorator

    Coroutines must be started, advanced to their first "yield" point,
    and this decorator does this automatically.
    """
    def startcr(*ar, **kw):
        cr = func(*ar, **kw)
        cr.next()
        return cr
    return startcr

@coroutine
def collector(storage):
    """Act as "sink" and collect all sent in @storage"""
    while True:
        storage.append((yield))

@coroutine      
def state_machine(sink):
    """ .send() new parts to be tokenized by the state machine,
    tokens are passed on to @sink
    """ 
    s = ""
    state = STATE_CODE
    while True: 
        if state is STATE_CODE :
            if "//" in s :
                sink.send((TOKEN_COMMENT, s.split( "//" )[1] ))
                state = STATE_COMMENT
            else :
                sink.send(( TOKEN_CODE, s ))
        if state is STATE_COMMENT :
            if "//" in s :
                sink.send(( TOKEN_COMMENT, s.split( "//" )[1] ))
            else
                state = STATE_CODE
                # re-evaluate same line
                continue
        s = (yield)

tokens = []
sm = state_machine(collector(tokens))
for piece in i:
    sm.send(piece)

Код вище збирає всі лексеми як кортежі, tokensі я припускаю, що немає різниці між оригінальним кодом .append()та .add()в ньому.


4
Як би ви написали це сьогодні в Python 3.x?
Noctis Skytower

13

Я це робив так:

condition = True
while condition:
     do_stuff()
     condition = (<something that evaluates to True or False>)

Мені це здається спрощеним рішенням, я здивований, що я його ще тут не бачив. Очевидно, це також можна перевернути

while not condition:

тощо.


Ви кажете: "Я здивований, що я його тут уже не бачив", - але я не бачу різниці, наприклад, з рішення порошкової колби 2010 року. Це точно так само. ("умова = Істинно, тоді як умова: # тіло циклу тут умова = test_loop_condition () # кінець циклу")
cslotty

10

для циклу "циклу", який містить операції спробу

loop = True
while loop:
    generic_stuff()
    try:
        questionable_stuff()
#       to break from successful completion
#       loop = False  
    except:
        optional_stuff()
#       to break from unsuccessful completion - 
#       the case referenced in the OP's question
        loop = False
   finally:
        more_generic_stuff()

альтернативно, коли немає необхідності в пункті "нарешті"

while True:
    generic_stuff()
    try:
        questionable_stuff()
#       to break from successful completion
#       break  
    except:
        optional_stuff()
#       to break from unsuccessful completion - 
#       the case referenced in the OP's question
        break

7
while condition is True: 
  stuff()
else:
  stuff()

8
Ew Це здається значно потворніше, ніж використання перерви.
mattdm

5
Це розумно, але воно stuffповинно бути функцією або для того, щоб тіло коду повторювалося.
Noctis Skytower

12
Все , що необхідно , так це while condition:тому , що is Trueмається на увазі.
мартіно

2
це не вдається, якщо conditionзалежить від якоїсь внутрішньої змінної stuff(), тому що ця змінна в цей момент не визначена.
йо

5
Не та ж логіка, тому що за останньою ітерацією, коли умова! = True: Він називає код остаточним часом. Де як Do Do , спочатку закликає код, після чого перевіряє стан перед повторним запуском. Зробити поки: виконати блок один раз; потім перевірити і повторно запустити цю відповідь: перевірити і повторно запустити; потім виконати блок коду один раз . Велика різниця!
Zv_oDD

7

Швидкий злом:

def dowhile(func = None, condition = None):
    if not func or not condition:
        return
    else:
        func()
        while condition():
            func()

Використовуйте так:

>>> x = 10
>>> def f():
...     global x
...     x = x - 1
>>> def c():
        global x
        return x > 0
>>> dowhile(f, c)
>>> print x
0

3

Чому ти просто не робиш

for s in l :
    print s
print "done"

?


1
мені потрібно створити державну машину. У державній машині це звичайний випадок, щоб переоцінити CURRENT оператор, тому мені потрібно «продовжити», не повторюючи наступний елемент. Я не знаю, як зробити таку річ у 'for s in l:' iteration :(. У циклі виконання часу 'продовження' буде переоцінено поточний елемент, ітерація в кінці.
grigoryvp

Тоді, чи можете ви визначити якийсь псевдокод для вашої державної машини, тож ми можемо натякнути вам на найкраще пітонічне рішення? Я не знаю багато про державні машини (і я, мабуть, не єдиний), тому, якщо ви трохи розкажете нам про свій алгоритм, нам це буде простіше допомогти.
Мартін

Бо цикл не працює для таких речей, як: a = fun () в той час як a == 'zxc': сон (10) a = fun ()
harry

Це повністю пропускає точку перевірки булевого стану
javadba

1

Подивіться, чи це допомагає:

Встановіть прапор всередині обробника винятків і перевірте його, перш ніж працювати над s.

flagBreak = false;
while True :

    if flagBreak : break

    if s :
        print s
    try :
        s = i.next()
    except StopIteration :
        flagBreak = true

print "done"

3
Можна спростити за допомогою while not flagBreak:та видалення if (flagBreak) : break.
мартіно

1
Я уникаю змінних з назвою - flagя не можу зробити висновок про те, що означає значення True або False. Натомість використовуйте doneабо endOfIteration. Код перетворюється на while not done: ....
IceArdor

1

Якщо у вас є сценарій, коли ви займаєтеся циклами, коли ресурс є недійсним або щось подібне, що кидає виняток, ви можете використовувати щось на зразок

import time

while True:
    try:
       f = open('some/path', 'r')
    except IOError:
       print('File could not be read. Retrying in 5 seconds')   
       time.sleep(5)
    else:
       break

0

Для мене типовим циклом while буде щось подібне:

xBool = True
# A counter to force a condition (eg. yCount = some integer value)

while xBool:
    # set up the condition (eg. if yCount > 0):
        (Do something)
        yCount = yCount - 1
    else:
        # (condition is not met, set xBool False)
        xBool = False

Я можу включити for..loop також в циклі while, якщо ситуація цього вимагає, для перегляду іншого набору умов.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.