Python'da “open” ile birden fazla dosyayı nasıl açabilirim?


672

Bir kerede birkaç dosyayı değiştirmek istiyorum, eğer hepsine yazabilirsem. Bir şekilde birden fazla açık çağrıları withifadeyle birleştirip birleştiremeyeceğimi merak ediyorum :

try:
  with open('a', 'w') as a and open('b', 'w') as b:
    do_something()
except IOError as e:
  print 'Operation failed: %s' % e.strerror

Bu mümkün değilse, bu soruna zarif bir çözüm nasıl olurdu?

Yanıtlar:


1052

Python 2.7 (veya sırasıyla 3.1) itibariyle yazabilirsiniz

with open('a', 'w') as a, open('b', 'w') as b:
    do_something()

Python'un önceki sürümlerinde, bazen contextlib.nested()içerik yöneticilerini yuvalamak için kullanabilirsiniz . Bu, birden çok dosyayı açmak için beklendiği gibi çalışmaz - ayrıntılar için bağlantılı belgelere bakın.


Değişken sayıda dosyayı aynı anda açmak istediğiniz nadir durumlarda contextlib.ExitStack, Python sürüm 3.3'ten başlayarak kullanabilirsiniz :

with ExitStack() as stack:
    files = [stack.enter_context(open(fname)) for fname in filenames]
    # Do something with "files"

Çoğu zaman değişken bir dosya grubunuz olsa da, muhtemelen birbiri ardına açmak istersiniz.


5
Ne yazık ki, contextlib.nested belgelerine göre, dosyayı açmak için kullanmamalısınız: "iki dosyayı açmak için nested () kullanmak bir programlama hatasıdır, çünkü dosya açılırken bir istisna atılırsa ilk dosya hemen kapatılmaz ikinci dosya. "
weronika

41
withdeğişken bir dosya listesini açmak için kullanmanın bir yolu var mı?
monkut

23
@monkut: Çok iyi bir soru (bunu ayrı bir soru olarak sorabilirsiniz). Kısa cevap: Evet, ExitStackPython 3.3'ten itibaren var. Bunu Python'un önceki herhangi bir sürümünde yapmanın kolay bir yolu yoktur.
Sven Marnach

12
Bu sözdiziminin birden çok satıra yayılması mümkün mü?
tommy.carstensen

9
@ tommy.carstensen: Alışılmış hat devam mekanizmalarını kullanabilirsiniz . Büyük olasılıkla, PEP 9 tarafından önerilen şekilde virgülün kırılması için ters eğik çizgi satırını kullanmalısınız .
Sven Marnach

99

Sadece yerini andile ,ve İşiniz bittiğinde:

try:
    with open('a', 'w') as a, open('b', 'w') as b:
        do_something()
except IOError as e:
    print 'Operation failed: %s' % e.strerror

3
Hangi Python sürümlerinin bu sözdizimini desteklediğini belirtmelisiniz.
Craig McQueen

58

Bir kerede veya uzun dosya yollarında birçok dosyayı açmak için, işleri birden çok satırdan ayırmak yararlı olabilir. Gönderen Python Stil Rehberi başka cevaba yorumlarında @Sven Marnach önerdiği gibi:

with open('/path/to/InFile.ext', 'r') as file_1, \
     open('/path/to/OutFile.ext', 'w') as file_2:
    file_2.write(file_1.read())

1
Bu girinti ile: "flake8: devam çizgisi görsel girinti için aşırı girintili"
Louis M

@LouisM Bu, temel python yerine düzenleyicinizden veya ortamınızdan gelen bir şey gibi geliyor. Sizin için sorun olmaya devam ederse, bununla ilgili yeni bir soru oluşturmanızı ve editörünüz ve ortamınız hakkında daha fazla ayrıntı vermenizi öneririm.
Michael Ohlrogge

3
Evet, kesinlikle editörüm ve bu sadece bir uyarı. Vurgulamak istediğim şey, girintinizin PEP8 ile uyumlu olmadığıdır. İkinci open () öğesini, birincisiyle hizalamak yerine 8 boşlukla girintilemelisiniz.
Louis M

2
@ Louis PEP8 kural değil , bir rehberdir ve bu durumda kesinlikle görmezden gelirim
Nick

2
Evet bununla ilgili bir sorun yok, otomatik tüy bırakan diğer insanlar için yararlı olabilir :)
Louis M

15

İç içe ifadeler ile aynı işi yapacak ve bence, başa çıkmak daha kolay.

Diyelim ki inFile.txt dosyasına sahipsiniz ve aynı anda iki outFile dosyasına yazmak istiyorsunuz.

with open("inFile.txt", 'r') as fr:
    with open("outFile1.txt", 'w') as fw1:
        with open("outFile2.txt", 'w') as fw2:
            for line in fr.readlines():
                fw1.writelines(line)
                fw2.writelines(line)

DÜZENLE:

Downvote nedenini anlamıyorum. Cevabımı yayınlamadan önce kodumu test ettim ve istendiği gibi çalışıyor: Soru sorulduğu gibi, tüm outFile'lara yazıyor. Yinelenen yazma veya yazma hatası yok. Bu yüzden cevabımın neden yanlış, yetersiz veya bunun gibi bir şey olarak değerlendirildiğini merak ediyorum.


1
Başka birisinin sizi ne düşürdüğünü bilmiyorum, ama bu sadece ihtiyacım olan şey olan üç dosya (bir girdi, iki çıktı) olan tek örnek olduğu için sizi UPVOTED.
Adam Michael Wood

2
belki Python> 2.6'da bcoz indirdiniz (neden daha fazla pythonic kodu yazabilirsiniz - gist.github.com/IaroslavR/3d8692e2a11e1ef902d2d8277eb88cb8 (neden yorumlara kod parçası ekleyemiyorum ?!) 2018'deyiz ;) Geçmişi
El Ruso

2
Poo-poohing python 2.6: CentOS 6'ya (Kasım 2020'ye kadar EOL olmayan) dostça bir hatırlatma, varsayılan olarak py2.6'yı kullanıyor. Yani bu cevap (şu an itibariyle) hala en iyi genel IMO.
BJ Black

11

Python 3.3'ten bu yana, rasgele sayıda dosyayı güvenle açmakExitStack için contextlibmodüldeki sınıfı kullanabilirsiniz .

İçeriğe duyarlı dinamik bir dizi nesneyi yönetebilir ; bu, kaç dosya işleyeceğinizi bilmiyorsanız özellikle yararlı olacağı anlamına gelir .

Aslında, belgelerde belirtilen standart kullanım durumu dinamik sayıda dosyayı yönetmektir.

with ExitStack() as stack:
    files = [stack.enter_context(open(fname)) for fname in filenames]
    # All opened files will automatically be closed at the end of
    # the with statement, even if attempts to open files later
    # in the list raise an exception

Ayrıntılarla ilgileniyorsanız, nasıl ExitStackçalıştığını açıklamak için genel bir örnek :

from contextlib import ExitStack

class X:
    num = 1

    def __init__(self):
        self.num = X.num
        X.num += 1

    def __repr__(self):
        cls = type(self)
        return '{cls.__name__}{self.num}'.format(cls=cls, self=self)

    def __enter__(self):
        print('enter {!r}'.format(self))
        return self.num

    def __exit__(self, exc_type, exc_value, traceback):
        print('exit {!r}'.format(self))
        return True

xs = [X() for _ in range(3)]

with ExitStack() as stack:
    print(len(stack._exit_callbacks)) # number of callbacks called on exit
    nums = [stack.enter_context(x) for x in xs]
    print(len(stack._exit_callbacks))

print(len(stack._exit_callbacks))
print(nums)

Çıktı:

0
enter X1
enter X2
enter X3
3
exit X3
exit X2
exit X1
0
[1, 2, 3]

3

Python 2.6 ile Çalışmaz, birden fazla dosyayı açmak için aşağıdaki yolu kullanmamız gerekir:

with open('a', 'w') as a:
    with open('b', 'w') as b:

1

Geç cevap (8 yıl), ancak birden fazla dosyayı bir dosyaya birleştirmek isteyen biri için aşağıdaki işlev yardımcı olabilir:

def multi_open(_list):
    out=""
    for x in _list:
        try:
            with open(x) as f:
                out+=f.read()
        except:
            pass
            # print(f"Cannot open file {x}")
    return(out)

fl = ["C:/bdlog.txt", "C:/Jts/tws.vmoptions", "C:/not.exist"]
print(multi_open(fl))

2018-10-23 19:18:11.361 PROFILE  [Stop Drivers] [1ms]
2018-10-23 19:18:11.361 PROFILE  [Parental uninit] [0ms]
...
# This file contains VM parameters for Trader Workstation.
# Each parameter should be defined in a separate line and the
...
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.