'With' ifadesinde birden çok değişken mi var?


391

withPython'da bir ifade kullanarak birden fazla değişken bildirmek mümkün mü ?

Gibi bir şey:

from __future__ import with_statement

with open("out.txt","wt"), open("in.txt") as file_out, file_in:
    for line in file_in:
        file_out.write(line)

... ya da aynı anda iki kaynağı temizlemek sorun mu?


Belki şöyle: f: olarak [ifade1, ifade2] ile ve sonra f [0] ve f [1] kullanın.
jbasko

İyi bir şey olurdu çünkü bir şey içe aktarmaya gerek yok .... ama işe yaramaz AttributeError: 'list' nesnesinin ' exit ' özelliği yok
pufferfish

Python'un kapanışları olsaydı, şu ifadeyle ihtiyacınız olmazdı
BT

With ifadesi kullanmanıza gerek yok , değil mi? Sadece file_out ve file_in parametrelerini None olarak ayarlayabilir, ardından bir try / hariç / nihayet onları açıp denemede işleyebilir ve daha sonra None değilse nihayetinde kapatabilirsiniz. Bunun için çift girinti gerekmez.
M Katz

1
Bu cevapların birçoğu, ifadelerle ikiden fazla ihtiyaç duyulmasıyla ilgilenmez. Teorik olarak onlarca bağlamı açmaya ihtiyaç duyan uygulamalar olabilir, yuvalama çok hızlı bir şekilde parçalanır ve herhangi bir satır uzunluğu sınırlaması uygulanır.
ThorSummoner

Yanıtlar:


667

Mümkün V3.1 ve Python 2.7'den bu yana Python 3'te . Yeni withsözdizimi birden çok içerik yöneticisini destekler:

with A() as a, B() as b, C() as c:
    doSomething(a,b,c)

Aksine contextlib.nested, bu garantiler ave bolacak onların __exit__()'bile denir C()ya' s __enter__()yöntemi bir özel durum oluşturur.

Daha sonraki tanımlarda önceki değişkenleri de kullanabilirsiniz ( aşağıdaki h / t Ahmad ):

with A() as a, B(a) as b, C(a, b) as c:
    doSomething(a, c)

1
alanları ifadedeki gibi bir şeye eşit olarak ayarlamak mümkün with open('./file') as arg.x = file:mü?
Charlie Parker

13
Ayrıca,
şunlar

sınıf testi2: x = 1; t2 = t2.x olarak açık ('f2.txt') olan test2 (): t2.x.readlines () içindeki l1 için: print (l1); # Charlie Parker # python 3.6'da test edildi
Ahmad Yoosofan

1
lütfen dikkat, asisteğe bağlıdır.
Sławomir Lenart

@ SławomirLenart'ın ne dediğini açıklığa kavuşturmak için: asnesneye ihtiyacınız varsa gereklidir, aya bda bütün as aveya as bgerekli değildir
Ciprian Tomoiagă

56

contextlib.nested bunu destekler:

import contextlib

with contextlib.nested(open("out.txt","wt"), open("in.txt")) as (file_out, file_in):

   ...

Güncelleme:
Aşağıdakilerle ilgili belgeleri alıntılamak için contextlib.nested:

2.7 sürümünden beri kullanımdan kaldırıldı : with-ifadesi artık bu işlevselliği doğrudan destekliyor (kafa karıştırıcı hata eğilimli tuhaflıklar olmadan).

Daha fazla bilgi için Rafał Dowgird'in cevabına bakınız .


34
Bunu söylediğim için üzgünüm, ancak nestedbağlam yöneticisinin bir hata olduğunu ve asla kullanılmaması gerektiğini düşünüyorum. Bu örnekte, ikinci dosyayı açmak bir istisna yaratırsa, ilk dosya hiç kapatılmaz, böylece bağlam yöneticilerini kullanma amacını tamamen yok eder.
Rafał Dowgird

Neden öyle diyorsun? Belgeler iç içe kullanımın iç içe 'ile eşdeğer olduğunu söylüyor
James Hopkin

@Rafal: kılavuza bir bakış, python'un with ifadelerini doğru şekilde iç içe geçirdiğini gösteriyor. Asıl sorun, ikinci dosya kapatıldıktan sonra bir istisna atarsa.
Bilinmiyor

10
@James: Hayır, docs.python.org/library/contextlib.html#contextlib.nested adresindeki dokümanlardaki eşdeğer kod , standart iç içe geçmiş withbloklardan farklıdır . Yöneticiler, bloklarla girmeden önce sırayla oluşturulur : m1, m2, m3 = A (), B (), C () B () veya C () istisna dışında başarısız olursa, A ( ) çöp toplayıcıdır.
Rafał Dowgird

8
2.7 sürümünden beri kullanımdan kaldırıldı . Not: with-ifadesi artık bu işlevselliği doğrudan desteklemektedir (kafa karıştırıcı hata eğilimli tuhaflıklar olmadan).
miku

36

Değişkenleri satırlara böldüğünüzde, yeni satırları kaydırmak için ters eğik çizgiler kullanmanız gerektiğini unutmayın.

with A() as a, \
     B() as b, \
     C() as c:
    doSomething(a,b,c)

Parantezler işe yaramaz, çünkü Python bunun yerine bir demet oluşturur.

with (A(),
      B(),
      C()):
    doSomething(a,b,c)

Tuples bir __enter__özniteliğe sahip olmadığından bir hata alırsınız (açıklayıcı değildir ve sınıf türünü tanımlamaz):

AttributeError: __enter__

asParantez içinde kullanmaya çalışırsanız , Python hatayı ayrıştırma zamanında yakalar:

with (A() as a,
      B() as b,
      C() as c):
    doSomething(a,b,c)

Sözdizimi Hatası: geçersiz sözdizimi

https://bugs.python.org/issue12782 bu sorunla ilgili gibi görünüyor.


16

Bunun yerine bunu yapmak istediğinizi düşünüyorum:

from __future__ import with_statement

with open("out.txt","wt") as file_out:
    with open("in.txt") as file_in:
        for line in file_in:
            file_out.write(line)

5
Şu anda böyle yapıyorum, ama sonra yuvalama istediğimden iki kat daha derin (demek) ...
pufferfish

Bence bu en temiz yaklaşım - diğer yaklaşımları okumak daha zor olacak. Alex Martelli'nin cevabı istediğiniz şeye daha yakın görünüyor ancak çok daha az okunabilir. Yuvalama neden böyle bir endişe kaynağıdır?
Andrew Hare

7
Kuşkusuz büyük bir şey değil, ama "bunu içe aktar" (aka "Python'un Zen'i") başına "düz iç içe olmaktan daha iyidir" - bu yüzden standart kütüphaneye contextlib.nested ekledik. BTW, 3.1 yeni bir sözdizimine sahip olabilir. t Mükemmel kabul edildi ... ancak istenmeyen yuvalamadan kaçınmak kesinlikle temel Python geliştiricileri arasında yaygın olarak paylaşılan bir hedeftir).
Alex Martelli

2
@Alex: Çok doğru ama aynı zamanda "Okunabilirlik önemlidir".
Andrew Hare

4
@Andrew: Sanırım bir girinti seviyesi, programın amaçlanan mantığını daha iyi ifade ediyor, bu da iki değişkeni "atomik olarak" oluşturmak ve daha sonra birlikte temizlemek (bunun aslında gerçekleşen şey olmadığını anlıyorum). İstisna sorununun bir anlaşma kırıcı olduğunu düşünün
pufferfish

12

Python 3.3'ten bu yana ExitStack, contextlibmodüldeki sınıfı kullanabilirsiniz .

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

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

İşte 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(stack._exit_callbacks)
    nums = [stack.enter_context(x) for x in xs]
    print(stack._exit_callbacks)
print(stack._exit_callbacks)
print(nums)

Çıktı:

deque([])
enter X1
enter X2
enter X3
deque([<function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f86158>, <function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f861e0>, <function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f86268>])
exit X3
exit X2
exit X1
deque([])
[1, 2, 3]

0

Python 3.1+ sürümünde birden çok bağlam ifadesi belirtebilirsiniz ve bunlar birden çok withifade yuvalanmış gibi işlenir :

with A() as a, B() as b:
    suite

eşittir

with A() as a:
    with B() as b:
        suite

Bu aynı zamanda ikinci ifadedeki ilk ifadenin diğer adını kullanabileceğiniz anlamına gelir (db bağlantıları / imleçlerle çalışırken yararlıdır):

with get_conn() as conn, conn.cursor() as cursor:
    cursor.execute(sql)
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.