Python'da bir dosyayı satır satır nasıl okumalıyım?


137

Tarih öncesi zamanlarda (Python 1.4):

fp = open('filename.txt')
while 1:
    line = fp.readline()
    if not line:
        break
    print line

Python 2.1'den sonra şunları yaptık:

for line in open('filename.txt').xreadlines():
    print line

Python 2.3'te uygun yineleyici protokolünü almadan önce şunları yapabiliriz:

for line in open('filename.txt'):
    print line

Daha ayrıntılı kullanarak bazı örnekler gördüm:

with open('filename.txt') as fp:
    for line in fp:
        print line

tercih edilen yöntem bu mu?

with ifadesi dosyanın kapatılmasını sağlar ... ama neden dosya nesneleri için yineleyici protokolüne dahil değil?


4
imho, son öneri öncekinden daha ayrıntılı değil. Sadece daha fazla iş yapar (işiniz bittiğinde dosyanın kapanmasını sağlar).
azhrei

1
@azhrei bir satır daha fazla, bu yüzden objektif olarak daha ayrıntılı.
thebjorn

7
Söylediklerinizi alıyorum ama sadece elmaları elmalarla karşılaştırmayı söylüyorum, mesajınızdaki ikinci son öneri de son seçeneğin ne yaptığını karşılamak için istisna işleme koduna ihtiyaç duyuyor. Yani pratikte daha ayrıntılı. Sanırım son iki seçenekten hangisinin en iyisi olduğu bağlama bağlı.
azhrei

Yanıtlar:


227

Aşağıdakilerin tercih edilmesinin tam bir nedeni vardır:

with open('filename.txt') as fp:
    for line in fp:
        print line

Hepimiz çöp toplama için CPython'un nispeten belirleyici referans sayma şeması ile şımarık. Python'un diğer varsayımsal uygulamaları, withbelleği geri kazanmak için başka bir şema kullanırlarsa , blok olmadan dosyayı "yeterince hızlı" kapatmayabilir.

Böyle bir uygulamada, kodunuz dosyaları çöp toplayıcının artık dosya tanıtıcılarında sonlandırıcıları çağırmasından daha hızlı açarsa işletim sisteminden "çok fazla dosya açık" hatası alabilirsiniz. Genel çözüm, GC'yi hemen tetiklemektir, ancak bu kötü bir hack'tir ve kütüphanelerde olanlar da dahil olmak üzere hatayla karşılaşabilecek her işlev tarafından yapılmalıdır . Ne kabustu ama.

Ya da sadece withbloğu kullanabilirsiniz .

Bonus soru

(Yalnızca sorunun nesnel yönleriyle ilgileniyorsanız şimdi okumayı bırakın.)

Neden dosya nesneleri için yineleyici protokolüne dahil edilmiyor?

Bu API tasarımı hakkında öznel bir soru, bu yüzden iki bölümde öznel bir cevap var.

Bağırsak seviyesinde bu yanlıştır, çünkü yineleyici protokolünün iki ayrı şey yapmasını sağlar - satırlar üzerinde yineleme ve dosya tanıtıcısını kapatma - ve genellikle basit görünümlü bir işlevin iki işlem yapması kötü bir fikirdir. Bu durumda, yineleyiciler bir dosyanın içeriğiyle yarı işlevsel, değer tabanlı bir şekilde ilişkili olduğu için özellikle kötü hissettirir, ancak dosya tanıtıcılarını yönetmek tamamen ayrı bir görevdir. Her ikisini de görünmez bir şekilde tek bir eylemde ezmek, kodu okuyan ve program davranışı hakkında akıl yürütmeyi zorlaştıran insanlar için şaşırtıcıdır.

Diğer diller de aynı sonuca varmıştır. Haskell kısaca, bir dosyanın üzerinde yinelemenizi ve akışın sonuna geldiğinizde otomatik olarak kapanmasını sağlayan "tembel IO" ile flört etti, ancak bu günlerde Haskell'de tembel IO kullanmanın neredeyse evrensel olarak cesareti kırıldı ve Haskell kullanıcılar çoğunlukla withPython'daki blok gibi davranan Conduit gibi daha açık kaynak yönetimine geçtiler .

Teknik düzeyde, yineleme dosya tanıtıcısını kapattığında da işe yaramayacak bir dosya tanıtıcısıyla yapmak isteyebileceğiniz bazı şeyler vardır. Örneğin, dosyayı iki kez yinelemem gerektiğini varsayalım:

with open('filename.txt') as fp:
    for line in fp:
        ...
    fp.seek(0)
    for line in fp:
        ...

Bu daha az yaygın bir kullanım durumu olsa da, başlangıçta ilk üç satırı içeren mevcut bir kod tabanına altta üç kod satırını eklemiş olabileceğimi düşünün. Yineleme dosyayı kapatsaydı, bunu yapamazdım. Bu nedenle, yineleme ve kaynak yönetimini ayrı tutmak, kod parçalarını daha büyük, çalışan bir Python programına dönüştürmeyi kolaylaştırır.

Oluşturulabilirlik, bir dilin veya API'nın en önemli kullanılabilirlik özelliklerinden biridir.


1
+1 çünkü op ;-) hakkındaki
yorumumdaki

alternatif uygulama ile bile, işleyici sadece yüzlerce dosyayı çok hızlı bir şekilde açan programlarda size sorun yaratacaktır. Çoğu program sarkan dosya referansı ile hiçbir sorun olmadan alabilirsiniz. Devre dışı bırakmazsanız, GC eninde sonunda bir anda başlayacak ve dosya tanıtıcısını temizleyecektir. withsize huzur veriyor, bu yüzden hala en iyi uygulama.
Yalan Ryan

1
@DietrichEpp: belki "sarkan dosya referansı" doğru kelimeler değildi, gerçekten artık erişilemeyen ancak henüz kapatılmayan dosya tanıtıcıları kastediyordum. Her durumda, GC, dosya nesnesini topladığında dosya tanıtıcısını kapatacaktır, bu nedenle dosya nesnesine fazladan başvurunuz olmadığı ve GC'yi devre dışı bırakmadığınız ve çok sayıda dosyayı hızlı bir şekilde açmamanız durumunda art arda dosyayı kapatmamanız nedeniyle "çok fazla dosya açıldı".
Yalan Ryan

1
Evet, "kodunuz dosyaları çöp toplayıcının artık dosya tanıtıcılarında sonlandırıcıları çağırdığından daha hızlı açarsa" demek istediğim tam olarak budur.
Dietrich Epp

1
Kullanmanın en büyük nedeni, dosyayı kapatmazsanız, hemen yazılmamasıdır.
Antimon

20

Evet,

with open('filename.txt') as fp:
    for line in fp:
        print line

gitmek için bir yoldur.

Daha ayrıntılı değil. Daha güvenlidir.


5

Ek satırdan kapalıysanız, aşağıdaki gibi bir sarma işlevi kullanabilirsiniz:

def with_iter(iterable):
    with iterable as iter:
        for item in iter:
            yield item

for line in with_iter(open('...')):
    ...

Python 3.3'te, yield fromifade bunu daha da kısaltır:

def with_iter(iterable):
    with iterable as iter:
        yield from iter

2
xreadlines işlevini çağırın .. ve xreadlines.py adlı bir dosyaya koyun ve Python 2.1 sözdizimine geri döndük :-)
thebjorn

@thebjorn: belki, ancak alıntıladığınız Python 2.1 örneği, alternatif uygulamalarda kapatılmamış dosya işleyicisinden güvenli değildi. Kapatılmamış dosya işleyicisinden güvenli bir Python 2.1 dosyası okuması en az 5 satır sürer.
Lie Ryan

-1
f = open('test.txt','r')
for line in f.xreadlines():
    print line
f.close()

5
Bu soruya gerçekten cevap vermiyor
Thayne
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.