Python’daki büyük bir dosyanın (yüz binlerce satır) satır sayısını almamız gerekirse ne yapabiliriz? Hem hafıza hem de zaman açısından en verimli yol nedir?
İlk akla gelen yol aşağıdaki gibi olabilir:
def file_len(fname):
with open(fname) as f:
for i, l in enumerate(f):
pass
return i + 1
Peki daha iyisini yapmak mümkün mü?
Bundan daha iyisini yapmak biraz zor.
Sonuçta, herhangi bir çözümün tüm dosyayı okuması, kaç tane \n
karakteri olduğunu anlaması ve bu sonucu döndürmesi gerekecek.
Tüm dosyayı okumadan bunu yapmanın daha iyi bir yolu muhtemelen yok. Dolayısıyla en iyi çözüm her zaman I /O-hızına bağlı olacak. Yapabileceğiniz en iyi şey gereksiz bellek kullanmadığınızdan emin olmak olacak. Yukarıdaki kod bunu düşünüyor gibi görünüyor.
Yine de farklı yöntemlere de göz atalım.
Öncelikle tek satırda bu işlemi yapabilirdik, bu kod yazımı açısından oldukça ferah olurdu:
num_lines = sum(1 for line in open('myfile.txt'))
Peki performans açısından neler yapabiliriz:
Perfplot analizine göre, arabelleğe alınmış okuma çözümü en hızlısı.
def buf_count_newlines_gen(fname):
def _make_gen(reader):
while True:
b = reader(2 ** 16)
if not b: break
yield b
with open(fname, "rb") as f:
count = sum(buf.count(b"\n") for buf in _make_gen(f.raw.read))
return count
Hızlı ve hafıza tasarruflu. Diğer çözümler ise yaklaşık 20 kat daha yavaş.

Grafiği oluşturmak için aşağıdaki kodu kullanabilirsiniz:
import mmap
import subprocess
from functools import partial
import perfplot
def setup(n):
fname = "t.txt"
with open(fname, "w") as f:
for i in range(n):
f.write(str(i) + "\n")
return fname
def for_enumerate(fname):
i = 0
with open(fname) as f:
for i, _ in enumerate(f):
pass
return i + 1
def sum1(fname):
return sum(1 for _ in open(fname))
def mmap_count(fname):
with open(fname, "r+") as f:
buf = mmap.mmap(f.fileno(), 0)
lines = 0
while buf.readline():
lines += 1
return lines
def for_open(fname):
lines = 0
for _ in open(fname):
lines += 1
return lines
def buf_count_newlines(fname):
lines = 0
buf_size = 2 ** 16
with open(fname) as f:
buf = f.read(buf_size)
while buf:
lines += buf.count("\n")
buf = f.read(buf_size)
return lines
def buf_count_newlines_gen(fname):
def _make_gen(reader):
b = reader(2 ** 16)
while b:
yield b
b = reader(2 ** 16)
with open(fname, "rb") as f:
count = sum(buf.count(b"\n") for buf in _make_gen(f.raw.read))
return count
def wc_l(fname):
return int(subprocess.check_output(["wc", "-l", fname]).split()[0])
def sum_partial(fname):
with open(fname) as f:
count = sum(x.count("\n") for x in iter(partial(f.read, 2 ** 16), ""))
return count
def read_count(fname):
return open(fname).read().count("\n")
b = perfplot.bench(
setup=setup,
kernels=[
for_enumerate,
sum1,
mmap_count,
for_open,
wc_l,
buf_count_newlines,
buf_count_newlines_gen,
sum_partial,
read_count,
],
n_range=[2 ** k for k in range(27)],
xlabel="num lines",
)
b.save("out.png")
b.show()