Czy jest jakiś wbudowany sposób, aby uzyskać długość iterowalnego w Pythonie?

Na przykład, pliki w Pythonie są iteracyjne - są iteracyjne nad wierszami w pliku. Chcę policzyć liczbę linii.

Jednym szybkim sposobem jest to zrobić:

lines = len(list(open(fname)))

To jednak wczytuje cały plik do pamięci (na raz). To raczej niszczy cel iteratora (który musi tylko zachować bieżącą linię w pamięci).

To nie działa:

lines = len(line for line in open(fname))
Ponieważ Generatory nie mają długości.

Czy Jest jakiś sposób, aby to zrobić bez zdefiniowania funkcja liczenia?

def count(i):
    c = 0
    for el in i: c += 1
    return c

EDIT: dla wyjaśnienia rozumiem, że cały plik trzeba będzie przeczytać! Po prostu nie chcę tego w pamięci na raz =).

Author: Claudiu, 2008-12-24

9 answers

Krótko o iteracji przez iterable i liczeniu liczby iteracji, nie. To sprawia, że jest to iterowalne, a nie lista. To nie jest problem specyficzny dla Pythona. Spójrz na klasyczną strukturę danych listy połączonej. Znalezienie długości jest operacją O (n), która polega na iteracji całej listy, aby znaleźć liczbę elementów.

Jak już wspomniałem mcrute, prawdopodobnie możesz zredukować swoją funkcję do:

def count_iterable(i):
    return sum(1 for e in i)

Oczywiście, jeśli definiujesz własne iterable obiekt zawsze możesz zaimplementować __len__ samodzielnie i zachować gdzieś liczbę elementów.

 56
Author: Kamil Kisiel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-24 06:23:35

Jeśli potrzebujesz liczby linijek, możesz to zrobić, nie znam lepszego sposobu na to:

line_count = sum(1 for line in open("yourfile.txt"))
 18
Author: mcrute,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-24 06:03:06

Używam tej redefinicji już od jakiegoś czasu:

def len(thingy):
    try:
        return thingy.__len__()
    except AttributeError:
        return sum(1 for item in iter(thingy))
 10
Author: ttepasse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-24 07:49:02

Absolutnie nie, z tego prostego powodu, że iteraby nie są gwarantowane, aby być skończone.

Rozważmy tę całkowicie legalną funkcję generatora:

def forever():
    while True:
        yield "I will run forever"

Próba obliczenia długości tej funkcji za pomocą len([x for x in forever()]) nie zadziała.

Jak zauważyłeś, głównym celem iteratorów / generatorów jest możliwość pracy na dużym zbiorze danych bez ładowania go do pamięci. Fakt, że nie można uzyskać natychmiastowej długości należy uznać za kompromis.

 8
Author: Triptych,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-24 06:54:08

Pakiet cardinality zapewnia wydajną funkcję count() oraz kilka powiązanych funkcji do liczenia i sprawdzania wielkości dowolnej iteracyjnej: http://cardinality.readthedocs.org/

import cardinality

it = some_iterable(...)
print(cardinality.count(it))

Wewnętrznie używa enumerate() i collections.deque() do przeniesienia całej rzeczywistej logiki pętli i liczenia na poziom C, co powoduje znaczne przyspieszenie pętli for w Pythonie.

 6
Author: wouter bolsterlee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-01 15:41:09

Okazuje się, że istnieje zaimplementowane rozwiązanie tego wspólnego problemu. Rozważ użycie funkcji ilen() z more_itertools.

more_itertools.ilen(iterable)

Przykład wydrukowania kilku linii w pliku (używamy instrukcji with, aby bezpiecznie obsłużyć zamykanie plików):

# Example
import more_itertools

with open("foo.py", "r+") as f:
    print(more_itertools.ilen(f))

# Output: 433

Ten przykład zwraca taki sam wynik jak rozwiązania przedstawione wcześniej dla sumowania linii w pliku:

# Equivalent code
with open("foo.py", "r+") as f:
    print(sum(1 for line in f))

# Output: 433
 3
Author: pylang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-24 16:44:11

Jeśli się nad tym zastanowisz, jak zaproponujesz znalezienie liczby linii w pliku bez czytania całego pliku dla nowych linii? Oczywiście, możesz znaleźć Rozmiar Pliku, a jeśli możesz zagwarantować, że długość linii wynosi x, możesz uzyskać liczbę linii w pliku. Ale jeśli nie masz jakiegoś ograniczenia, nie widzę, jak to może działać. Ponadto, ponieważ iteraby mogą być nieskończenie długie...

 0
Author: Nikron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-24 07:11:22

Do filtrowania można użyć tej odmiany:

sum(is_good(item) for item in iterable)

, które można oczywiście odczytać jako "count good items" I jest krótsze i prostsze (choć być może mniej idiomatyczne) niż:

sum(1 for item in iterable if is_good(item)))

Uwaga: fakt, że True ocenia do 1 w kontekstach liczbowych jest określony w dokumentach ( https://docs.python.org/3.6/library/stdtypes.html#boolean-values ), więc ten przymus nie jest hack (w przeciwieństwie do niektórych innych języków, takich jak C / C++).

 0
Author: Kirill Bulygin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-26 06:12:42

Zrobiłem test pomiędzy dwoma wspólnymi procedurami w moim kodzie, który sprawdza, ile jest wykresów na N wierzchołkach, aby zobaczyć, która metoda zliczania elementów Wygenerowanej listy idzie szybciej. Sage posiada generator grafów (N), który generuje wszystkie wykresy na n wierzchołkach. Stworzyłem dwie funkcje, które uzyskują długość listy uzyskanej przez iteratora na dwa różne sposoby i zmierzyły każdą z nich (średnio ponad 100 uruchomień testowych) za pomocą czasu.funkcja time (). Funkcje były następujące "follows": {]}

def test_code_list(n):
    l = graphs(n)
    return len(list(l))

I

def test_code_sum(n):
    S = sum(1 for _ in graphs(n))
    return S

Teraz czas każdej metody

import time

t0 = time.time()
for i in range(100):
    test_code_list(5)
t1 = time.time()

avg_time = (t1-t0)/10

print 'average list method time = %s' % avg_time


t0 = time.time()
for i in range(100):
    test_code_sum(5)
t1 = time.time()

avg_time = (t1-t0)/100

print "average sum method time = %s" % avg_time

Average list method time = 0.0391882109642

Średni czas metody sum = 0.0418473792076

Obliczając w ten sposób liczbę wykresów na n = 5 wierzchołkach, metoda list jest nieco szybsza (chociaż 100 przebiegów testowych nie jest zbyt dużą wielkością próbki). Ale kiedy zwiększyłem długość wyliczanej listy, próbując wykresów na n = 7 wierzchołkach(tj. zmieniając wykresy(5) na wykresy (7)), wynik był to:

Średni czas metody listy = 4.14753051996

Średni czas metody sum = 3.96504004002

W tym przypadku metoda sum była nieco szybsza. Podsumowując, dwie metody są w przybliżeniu takie same prędkości, ale różnica może zależeć od długości listy (może być również po prostu, że tylko uśrednione ponad 100 przebiegów testowych, co nie jest bardzo wysokie-zajęłoby wieczność inaczej).
 -1
Author: Casey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-13 03:22:31