Python "with" ifadesi ne için tasarlanmıştır?


418

withBugün ilk kez Python bildirisiyle karşılaştım. Python'u birkaç aydır hafifçe kullanıyorum ve varlığını bile bilmiyordum! Biraz belirsiz durumu göz önüne alındığında, sormaya değer olacağını düşündüm:

  1. Python withifadesi ne için kullanılmak üzere tasarlanmıştır?
  2. Ne için kullanıyorsun?
  3. Dikkat etmem gereken herhangi bir gotcha veya kullanımı ile ilişkili yaygın anti-kalıplar var mı? Daha iyi kullanımıdır Herhangi vakalar try..finallydaha with?
  4. Neden daha yaygın kullanılmıyor?
  5. Hangi standart kütüphane sınıfları onunla uyumludur?

5
Sadece kayıt için, buradawith Python 3 belgelerinde.
Alexey

Java arka plandan gelen, bu karşılık gelen "denemede olarak hatırlamamı yardımcı olan bu tamamen doğru olmayabilir bile, Java kaynaklar".
vefthym

Yanıtlar:


399
  1. Bunun benden önce diğer kullanıcılar tarafından zaten yanıtlandığına inanıyorum, bu yüzden sadece tamlık uğruna ekliyorum: withifade, bağlam yöneticileri olarak adlandırılan ortak hazırlık ve temizleme görevlerini kapsülleyerek istisna işlemeyi basitleştirir . Daha fazla ayrıntı PEP 343'te bulunabilir . Örneğin, openifade kendi başına bir bağlam yöneticisidir, bu da bir dosyayı açmanıza, yürütmeyi withkullandığınız ifadenin bağlamında olduğu sürece açık tutmanıza ve bağlamdan çıkar çıkmaz onu kapatmanıza izin verir. bir istisna nedeniyle veya düzenli kontrol akışı sırasında bırakıp bırakmadığınız önemli değildir. Dolayısıyla withifade, C ++ ' da RAII modeline benzer şekillerde kullanılabilir : bazı kaynaklarwithve withbağlamdan ayrıldığınızda serbest bırakılır .

  2. Bazı örnekler şunlardır: dosyaları kullanarak açmak with open(filename) as fp:, kullanarak kilitleri almak with lock:(nerede lockbir örneğidir threading.Lock). contextmanagerDekoratörünü kullanarak kendi bağlam yöneticilerinizi de oluşturabilirsiniz contextlib. Örneğin, geçerli dizini geçici olarak değiştirmek ve sonra bulunduğum yere geri dönmek zorunda kaldığımda bunu sık sık kullanıyorum:

    from contextlib import contextmanager
    import os
    
    @contextmanager
    def working_directory(path):
        current_dir = os.getcwd()
        os.chdir(path)
        try:
            yield
        finally:
            os.chdir(current_dir)
    
    with working_directory("data/stuff"):
        # do something within data/stuff
    # here I am back again in the original working directory

    İşte geçici olarak yönlendirir başka bir örnek sys.stdin, sys.stdoutve sys.stderrbazı diğer dosya sap ve daha sonra geri yüklemeler onları için:

    from contextlib import contextmanager
    import sys
    
    @contextmanager
    def redirected(**kwds):
        stream_names = ["stdin", "stdout", "stderr"]
        old_streams = {}
        try:
            for sname in stream_names:
                stream = kwds.get(sname, None)
                if stream is not None and stream != getattr(sys, sname):
                    old_streams[sname] = getattr(sys, sname)
                    setattr(sys, sname, stream)
            yield
        finally:
            for sname, stream in old_streams.iteritems():
                setattr(sys, sname, stream)
    
    with redirected(stdout=open("/tmp/log.txt", "w")):
         # these print statements will go to /tmp/log.txt
         print "Test entry 1"
         print "Test entry 2"
    # back to the normal stdout
    print "Back to normal stdout again"

    Ve son olarak, geçici bir klasör oluşturan ve bağlamdan çıkarken onu temizleyen başka bir örnek:

    from tempfile import mkdtemp
    from shutil import rmtree
    
    @contextmanager
    def temporary_dir(*args, **kwds):
        name = mkdtemp(*args, **kwds)
        try:
            yield name
        finally:
            shutil.rmtree(name)
    
    with temporary_dir() as dirname:
        # do whatever you want

20
RAII ile karşılaştırmayı eklediğiniz için teşekkür ederiz. Bilmem gereken her şeyi anlatan bir C ++ programcısı olarak.
Fred Thomsen

Tamam, açıklığa kavuşturayım. İfadenin, withaltındaki talimatlar tamamlanana kadar bir değişkeni veriyle doldurmak ve daha sonra değişkeni serbest bırakmak için tasarlandığını mı söylüyorsunuz ?
Musixauce3000

Çünkü bunu bir py betiği açmak için kullandım. with open('myScript.py', 'r') as f: pass. Ben değişkeni çağırmak mümkün olması bekleniyor fbu belge atanmış olsaydı görünür ne kadar, belgenin metin içeriğini görmek için fdüzenli aracılığıyla openaçıklamada: f = open('myScript.py').read(). Ama bunun yerine aşağıdaki var: <_io.TextIOWrapper name='myScript.py' mode='r' encoding='cp1252'>. Bunun anlamı ne?
Musixauce3000

3
@ Musixauce3000 - kullanarak gerçek dosyaya witholan gereksinimi ortadan kaldırmaz read. withÇağrılar open- bu onunla yapmanız gerekenler bilmiyor - Bir örneği için aramak yapmak isteyebilirsiniz.
Tony Suffolk 66

@ Musixauce3000 İfadenin withaltındaki yönergeler tamamlanana kadar bir değişkeni verilerle doldurabilir veya çevrede başka değişiklikler yapabilir ve ardından gereken her türlü temizlemeyi yapabilir. Yapılabilecek temizleme türleri, açık bir dosyayı kapatmak veya @Tamas'ın bu örnekte olduğu gibi, dizinleri daha önce bulunduğunuz yere geri döndürmek vb. kullanım örneği. withgenellikle diğer temizlik türleri için kullanılır.
Bob Steinke

89

İki ilginç ders öneririm:

1.with ifadesi bir bağlam yöneticisi tarafından tanımlanan yöntem ile olan bloğun sarmak için kullanılmaktadır. Bu, try...except...finallyrahat kullanım için ortak kullanım modellerinin kapsüllenmesini sağlar.

2. Şöyle bir şey yapabilirsiniz:

with open("foo.txt") as foo_file:
    data = foo_file.read()

VEYA

from contextlib import nested
with nested(A(), B(), C()) as (X, Y, Z):
   do_something()

VEYA (Python 3.1)

with open('data') as input_file, open('result', 'w') as output_file:
   for line in input_file:
     output_file.write(parse(line))

VEYA

lock = threading.Lock()
with lock:
    # Critical section of code

3. Burada hiç Antipattern görmüyorum.
Aktaran Python Dive into :

deneyin ... son derece iyidir. ile daha iyidir.

4. Sanırım programcıların try..catch..finallybaşka dillerden ifadeleri kullanma alışkanlığıyla ilgili .


4
İş parçacığı senkronizasyon nesneleri ile uğraşırken gerçekten kendi başına gelir. Python'da nispeten nadirdir, ancak ihtiyacınız olduğunda gerçekten ihtiyacınız vardır with.
detly

1
diveintopython.org kapalı (kalıcı olarak?). En Aynalı diveintopython.net
sokulur

İyi bir yanıt örneği, açık dosya, açılış, io, dosya işlemlerinin perde arkasında özel bir referans adıyla temiz bir şekilde gizlenmiş olan başlıca bir örnektir
Angry 84

40

Python withdeyimi, Resource Acquisition Is InitializationC ++ 'da yaygın olarak kullanılan deyimin yerleşik dil desteğidir . İşletim sistemi kaynaklarının güvenli bir şekilde alınmasına ve serbest bırakılmasına izin verilmesi amaçlanmıştır.

withİfadesi bir kapsam / blok içerisindeki kaynaklar oluşturur. Kodunuzu blok içindeki kaynakları kullanarak yazarsınız. Bloktan çıkıldığında, bloktaki kodun sonucundan bağımsız olarak kaynaklar temiz bir şekilde serbest bırakılır (yani, blok normal olarak mı yoksa bir istisna nedeniyle mi çıkar).

Python kitaplığındaki withifadenin gerektirdiği protokole uyan ve böylece kutusundan çıkar çıkmaz kullanılabilen birçok kaynak . Ancak herkes, iyi belgelenmiş protokolü uygulayarak bir with ifadesinde kullanılabilecek kaynakları yapabilir: PEP 0343

Uygulamanızda dosyalar, ağ bağlantıları, kilitler ve benzerleri gibi açıkça bırakılması gereken kaynakları elde ettiğinizde kullanın.


27

Yine bütünlük için withifadeler için en kullanışlı kullanım durumumu ekleyeceğim .

Çok fazla bilimsel bilgi işlem yapıyorum ve bazı aktiviteler Decimaliçin keyfi hassas hesaplamalar için kütüphaneye ihtiyacım var . Kodumun bir kısmı yüksek hassasiyete ihtiyacım var ve diğer çoğu parça için daha az hassasiyete ihtiyacım var.

Varsayılan hassasiyetimi düşük bir sayıya ayarladım ve sonra withbazı bölümler için daha kesin bir cevap almak için kullanıyorum:

from decimal import localcontext

with localcontext() as ctx:
    ctx.prec = 42   # Perform a high precision calculation
    s = calculate_something()
s = +s  # Round the final result back to the default precision

Bunu, çok sayıda form faktöriyelerine neden olan Hipergeometrik Test ile çok kullanıyorum. Genomik ölçek hesaplamaları yaparken, yuvarlama ve taşma hatalarına dikkat etmelisiniz.


26

Bir antipattern örneği , ilmeğin dışına withsahip olmanın daha verimli olacağı bir halkanın iç kısmını kullanmak olabilir.with

Örneğin

for row in lines:
    with open("outfile","a") as f:
        f.write(row)

vs

with open("outfile","a") as f:
    for row in lines:
        f.write(row)

İlk yol, her biri için dosyayı açmak ve kapatmak, rowbu da ikinci yolla karşılaştırıldığında performans sorunlarına neden olabilir ve dosyayı bir kez açar ve kapatır.


10

Bkz. PEP 343 - 'with' ifadesi , sonunda bir örnek bölüm var.

... try / nihayet ifadelerinin standart kullanımlarını hesaba katmayı mümkün kılmak için Python diline yeni "with" ifadesi.


5

1, 2 ve 3. maddeler makul derecede iyi ele alınmıştır:

4: nispeten yeni, sadece python2.6 + (veya python2.5 kullanarak from __future__ import with_statement)



3

Kullanıma hazır destek için bir başka örnek ve yerleşik open()davranış biçimine alıştığınızda ilk başta biraz şaşırtıcı olabilecek bir örnek , aşağıdakiler connectiongibi popüler veritabanı modüllerinin nesneleridir:

connectionNesneleri bağlam yöneticileri ve gibi dışı Kutudan kullanılabilecek bir de with-statement, bununla birlikte, yukarıda not bu kullanarak:

Bir with-blockistisna ile veya istisnasız tamamlandığında, bağlantı kapatılmaz . with-blockİstisna ile bitirilmesi durumunda işlem geri alınır, aksi takdirde işlem yapılır.

Bu, programcının bağlantıyı kendileri kapatmaya özen göstermesi gerektiği anlamına gelir, ancak psycopg2 belgelerindewith-statements gösterildiği gibi bir bağlantı elde etmesine ve çoklu olarak kullanmasına izin verir :

conn = psycopg2.connect(DSN)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL1)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL2)

conn.close()

Yukarıdaki örnekte, öğelerinin cursornesnelerinin psycopg2de bağlam yöneticisi olduğunu göreceksiniz . Davranışla ilgili belgelerden:

Bir cursorçıkış with-blockkapandığında, sonunda ilişkilendirilmiş herhangi bir kaynağı serbest bırakır. İşlemin durumu etkilenmez.


3

Python'da genellikle “ with ” ifadesi bir dosyayı açmak, dosyadaki verileri işlemek ve ayrıca close () yöntemini çağırmadan dosyayı kapatmak için kullanılır. “With” ifadesi, temizleme etkinlikleri sağlayarak kural dışı durum işlemeyi kolaylaştırır.

İle genel şekli:

with open(“file name”, mode”) as file-var:
    processing statements

not: file-var.close () üzerine close () öğesini çağırarak dosyayı kapatmanıza gerek yoktur

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.