2016-09-06 10 views
5

Piszę skrypt, w którym muszę przetestować liczby pod pewnymi warunkami. Jeśli warunki są spełnione, chcę zwrócić True i chcę to zrobić w najszybszy możliwy sposób.Czy ktoś() jest leniwie oceniany?

Moim pierwszym pomysłem było użycie any() zamiast zagnieżdżonych if instrukcji lub wielu or łączących moje warunki. Ponieważ byłbym usatysfakcjonowany, gdyby którykolwiek z warunków był True mógłbym naprawdę skorzystać z tego, że any() jest leniwy i powraca Prawdziwy tak szybko, jak tylko mógł.

Na podstawie faktu, że poniższy wydruk następuje natychmiast, a nie po 10 (= 0 + 1 + 2 + 3 + 4) sekundach, zakładam, że tak jest. Czy to przypadek, czy też jestem w błędzie?

import time 

def some(sec): 
    time.sleep(sec) 
    return True 

print(any(some(x) for x in range(5))) 
+4

Tak - jest leniwy ... –

+0

dzięki! To samo dotyczy 'all()' i wyobrażam sobie –

+0

Tak, poprawne ... –

Odpowiedz

12

Tak, any() i all() zwarcie, przerywanie jak tylko wynik jest jasny: Zobacz docs:

wszystkie (iterable)

zwraca True jeśli wszystkie elementy liczby iteracji są prawdziwe (lub jeśli iteracja jest pusta). Odpowiednik:

def all(iterable): 
    for element in iterable: 
     if not element: 
      return False 
    return True 

any (iterable)

zwraca True jeśli każdy element iterowalny jest prawdą. Jeśli liczba iterowalna jest pusta, zwracamy false. Odpowiednik:

def any(iterable): 
    for element in iterable: 
     if element: 
      return True 
    return False 
+1

Zwykle znajduję dokumenty złe, ale w tym przypadku jest to nie do wyjaśnienia. Wielkie dzięki! Również to właśnie miałem na myśli pod względem _lazy evaluation_. w jaki sposób _short-circuiting_ jest inny? –

+1

Wyrażenie generatora jest leniwy w tym sensie, że wywołuje tylko 'some (x)', gdy żądany jest następny element. Zwarcie "dowolne/wszystkie" w tym sensie, że będzie żądało tylko przedmiotów, dopóki wynik nie będzie wyraźny. Można powiedzieć, że 'any ([some (x) for x in range (5)]) nadal ma zwarcia, ale nie jest leniwy, ponieważ zrozumienie listy powoduje, że' some' jest wywoływane we wszystkich elementach 'range (x) 'before' any' zaczyna się, ale 'any' nadal przestaje analizować listę wejściową tak szybko jak to możliwe. – chepner

4

Chociaż all() i any() funkcje zwarcie pierwszego „prawdziwego” element iterowalny sama iterowalny mogą być skonstruowane w sposób nie-lazy sposób. Rozważmy następujący przykład:

>> any(x == 100 for x in range(10**8)) 
True 

To potrwa kilka sekund, aby wykonać w Pythonie 2 jako range(10**8) konstruuje listę 10 ** 8 elementów. To samo wyrażenie uruchamia się natychmiast w Pythonie 3, gdzie range() jest leniwy.

+1

Dobra obserwacja –

+0

Uważam to za mylące, ponieważ wiem, że 'range()' tworzy pełną listę w python2, ale następnie owijamy ją w generator, który jest leniwy –

+0

@Chris_Rands: Kiedy używasz * wyrażenia generującego *, takiego jak w powyższym przykładzie wyrażenie "for" jest natychmiast obliczane. W związku z tym 'range()' tworzy pełną listę, nawet przed uruchomieniem generatora. –

2

Tak, to leniwy jak wykazano brzmienie:

def some(x, result=True): 
    print(x) 
    return result 

>>> print(any(some(x) for x in range(5))) 
0 
True 

>>> print(any(some(x, False) for x in range(5))) 
0 
1 
2 
3 
4 
False 

W pierwszym biegu any() zatrzymany po przetestowaniu pierwszy element, to znaczy zwarcie ocenę.

W drugim przebiegu any() kontynuowano testowanie aż do wyczerpania sekwencji.

2

Tak, i tu jest eksperymentem, który pokazuje, że nawet bardziej niż ostatecznie eksperymentu rozrządu:

import random 

def some(x): 
    print(x, end = ', ') 
    return random.random() < 0.25 

for i in range(5): 
    print(any(some(x) for x in range(10))) 

typowy run:

0, 1, 2, True 
0, 1, True 
0, True 
0, 1, 2, 3, True 
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, False 
3

Jak Tim poprawnie wspomniano, any i all zrobić krótko- Obwód, ale w twoim kodzie, co powoduje, że jest to leniwe jest użycie generators. Na przykład, ten kod nie leń:

print(any([slow_operation(x) for x in big_list])) 

lista będzie wykończona i obliczone, a dopiero potem przekazywane jako argument any.

Generatory, z drugiej strony, są iterables, które obliczyć każdą pozycję na żądanie. Mogą być one expressions, functions lub czasami ręcznie wdrażane jako leniwe iterators.

+0

ładny. Dzięki! –

Powiązane problemy