Python: сделать r = random_stuff (), пока не соответствует условиям (r) - PullRequest
2 голосов
/ 10 ноября 2011

Мне часто приходится случайным образом генерировать вещи с определенными ограничениями. Во многих случаях быстрее игнорировать ограничения в генерации, проверить, соблюдаются ли они впоследствии, и повторить процесс в противном случае. Не имея ключевого слова do, я обычно пишу

r = random_stuff()
while not meets_condition(r):
    r = random_stuff()

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

r = random_stuff() until meets_condition(r)

аналогично троичному оператору, введенному в 2.5:

a = b if condition else c

Именно здесь условие вычисляется перед выполнением левой части оператора. У кого-нибудь есть предложение по шаблону проектирования (должен работать в Python 2.7), который исправляет while -конструктивное внутреннее непитоническое уродство?

Ответы [ 5 ]

8 голосов
/ 10 ноября 2011
while True:
    r = random_stuff()
    if meets_condition(r):
        break

или

condition = True
while condition:
    r = random_stuff()
    condition = not meets_condition(r)
1 голос
/ 10 ноября 2011

Ваша идея неплоха - но не с новым ключевым словом until, а скорее как

a = (<expression> while <condition>)

расширение идеи выражений генератора .

Так как они не существуют, это вам не поможет.


Но то, что вы можете использовать, это iter функция с дозором.

dummy_sentinel = object()
for r in iter(random_stuff, dummy_sentinel):
    if meets_condition(r): break

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

for r in iter(random_stuff, None):
    if meets_condition(r): break

Затем вызывается random_stuff(), пока не будет выполнено условие.


Еще лучше может быть

r = next(r for r in iter(random_stuff, None) if meets_condition(r))

, который дает вам первый соответствующий.

0 голосов
/ 11 ноября 2011

Хорошо, вдохновленный @jaime, я написал следующий декоратор:

def retry(condition):
    def deco_retry(f):
        def f_retry(*args, **kwargs):
            success = False
            while not success:
                result = f(*args, **kwargs)
                success = condition(result)
            return result
        return f_retry
    return deco_retry

Теперь работают следующие:

def condition(calue):    
    return value < .5

@retry(condition)
def random_stuff():
    return random.random()

print random_stuff()

Кроме того, в строке:

@retry(lambda x: x < .5)
def random_stuff():
    return random.random()

print random_stuff()

Однако повторная попытка теперь связана с методом random_stuff(), который теперь можно использовать только с условием, с которым он был оформлен. Кроме того, он не работает для методов экземпляра (как в @retry(self.condition)). Любые идеи, чтобы обойти это?

0 голосов
/ 10 ноября 2011

Может быть, синтаксический сахар - это то, что доктор прописал? Вы могли бы сделать что-то вроде этого.

Мне было лень обрабатывать аргументы kw в find_condition. Вы получаете то, за что платите: D.

def find_condition(cond_mk, cond_ck, *args):
    """
    .. function:: find_condition(cond_mk, cond_ck) -> cond

       Create conditions by calling cond_mk until one is found that passes
       the condition check, cond_ck.  Once cond_ck returns True, iteration 
       over cond_mk stops and the last value processed is returned.

       ** WARNING **
       This function could loop infinitely.

       ``cond_mk`` - callable that creates a condition.  It's return value is
       passed to cond_ck for verification.

       ``cond_ck`` - callable that checks the return value of cond_mk.  

       ``args`` - any arguments to pass to cond_mk should be supplied here.
    """
    v = cond_mk(*args)
    while not cond_ck(v):
        v = cond_mk(*args)
    return v

# Test it out..
import random
random.seed()
print find_condition(random.randint, lambda x: x > 95, 1, 100)
0 голосов
/ 10 ноября 2011
while not meets_condition(random_stuff()): pass

Если вам действительно нужен random_stuff(), то его можно сохранить в другом месте в качестве побочного эффекта (например, сделать random_stuff метод __call__ класса).

...