Untuk apa pernyataan python “with” dirancang?

419

Saya menemukan withpernyataan Python untuk pertama kalinya hari ini. Saya telah menggunakan Python ringan selama beberapa bulan dan bahkan tidak tahu keberadaannya! Mengingat statusnya yang agak tidak jelas, saya pikir patut bertanya:

  1. Untuk apa withpernyataan Python dirancang untuk digunakan?
  2. Untuk apa Anda menggunakannya?
  3. Apakah ada gotcha yang perlu saya ketahui, atau anti-pola umum yang terkait dengan penggunaannya? Adakah kasus yang lebih baik digunakan try..finallydaripada with?
  4. Mengapa tidak digunakan lebih luas?
  5. Kelas perpustakaan standar apa yang kompatibel dengannya?
fmark
sumber
5
Sebagai catatan, ini adalahwith dokumentasi Python 3.
Alexey
berasal dari latar belakang Jawa, ini membantu saya untuk mengingatnya sebagai "try sesuai dengan sumber daya" di Jawa, bahkan jika yang mungkin tidak sepenuhnya benar.
vefthym

Jawaban:

399
  1. Saya percaya ini sudah dijawab oleh pengguna lain sebelum saya, jadi saya hanya menambahkannya demi kelengkapan: withpernyataan tersebut menyederhanakan penanganan pengecualian dengan merangkum persiapan umum dan tugas pembersihan dalam apa yang disebut manajer konteks . Rincian lebih lanjut dapat ditemukan di PEP 343 . Misalnya, openpernyataan itu sendiri adalah manajer konteks, yang memungkinkan Anda membuka file, tetap membuka selama eksekusi dalam konteks withpernyataan di mana Anda menggunakannya, dan tutup segera setelah Anda meninggalkan konteks, tidak peduli apakah Anda telah meninggalkannya karena pengecualian atau selama aliran kontrol reguler. The withpernyataan demikian dapat digunakan dengan cara yang mirip dengan pola RAII di C ++: beberapa sumber daya diperoleh olehwithpernyataan dan dirilis ketika Anda meninggalkan withkonteks.

  2. Beberapa contoh adalah: membuka file menggunakan with open(filename) as fp:, memperoleh kunci menggunakan with lock:(di mana lockmerupakan instance dari threading.Lock). Anda juga dapat membuat manajer konteks sendiri menggunakan contextmanagerdekorator contextlib. Misalnya, saya sering menggunakan ini ketika saya harus mengubah direktori saat ini sementara dan kemudian kembali ke tempat saya sebelumnya:

    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

    Berikut adalah contoh lain yang sementara dialihkan sys.stdin, sys.stdoutdan sys.stderrke beberapa file lain menangani dan mengembalikannya nanti:

    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"

    Dan akhirnya, contoh lain yang membuat folder sementara dan membersihkannya ketika meninggalkan konteks:

    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
Tamas
sumber
20
Terima kasih telah menambahkan perbandingan ke RAII. Sebagai seorang programmer C ++ yang memberi tahu saya semua yang perlu saya ketahui.
Fred Thomsen
Oke, jadi saya jelaskan ini. Anda mengatakan bahwa withpernyataan itu dirancang untuk mengisi variabel dengan data sampai instruksi di bawahnya selesai, dan kemudian membebaskan variabel?
Musixauce3000
Karena saya menggunakannya untuk membuka skrip py. with open('myScript.py', 'r') as f: pass. Aku diharapkan dapat memanggil variabel funtuk melihat isi teks dokumen, karena ini adalah apa yang akan muncul jika dokumen tersebut ditugaskan untuk fmelalui biasa openpernyataan: f = open('myScript.py').read(). Tapi bukannya aku berikut: <_io.TextIOWrapper name='myScript.py' mode='r' encoding='cp1252'>. Apa artinya?
Musixauce3000
3
@ Musixauce3000 - menggunakan withtidak menghapus kebutuhan untuk readfile yang sebenarnya. The withpanggilan open- tidak tahu apa yang perlu Anda lakukan dengan itu - Anda mungkin ingin melakukan mencari misalnya.
Tony Suffolk 66
@ Musixauce3000 withPernyataan ini dapat mengisi variabel dengan data atau membuat beberapa perubahan lain ke lingkungan sampai instruksi di bawahnya selesai, dan kemudian melakukan segala jenis pembersihan yang diperlukan. Jenis pembersihan yang dapat dilakukan adalah hal-hal seperti menutup file yang terbuka, atau seperti @Tamas miliki dalam contoh ini, mengubah direktori kembali ke tempat Anda sebelumnya, dll. Karena Python memiliki pengumpulan sampah, membebaskan variabel tidak penting gunakan kasing. withumumnya digunakan untuk jenis pembersihan lainnya.
Bob Steinke
89

Saya akan menyarankan dua kuliah yang menarik:

  • PEP 343 Pernyataan "dengan"
  • Effbot Memahami pernyataan "dengan" Python

1. The withpernyataan digunakan untuk membungkus eksekusi blok dengan metode yang didefinisikan oleh manajer konteks. Ini memungkinkan try...except...finallypola penggunaan umum untuk dienkapsulasi agar nyaman digunakan kembali.

2. Anda dapat melakukan sesuatu seperti:

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

ATAU

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

ATAU (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))

ATAU

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

3. Saya tidak melihat Antipattern di sini.
Mengutip Menyelam ke Python :

coba .. akhirnya baik. dengan lebih baik.

4. Saya kira itu terkait dengan kebiasaan programmer untuk menggunakan try..catch..finallypernyataan dari bahasa lain.

systempuntoout
sumber
4
Ini benar-benar muncul dengan sendirinya ketika Anda berhadapan dengan objek sinkronisasi threading. Relatif langka di Python, tetapi ketika Anda membutuhkannya, Anda benar-benar membutuhkannya with.
detly
1
diveintopython.org sedang down (permanen?) Dicerminkan di diveintopython.net
meringkuk
Contoh jawaban yang baik, file terbuka adalah contoh utama yang menunjukkan di balik layar pembukaan, io, menutup operasi file yang disembunyikan dengan rapi dengan nama referensi khusus
Angry 84
40

withPernyataan Python adalah dukungan bahasa bawaan dari Resource Acquisition Is Initializationidiom yang biasa digunakan dalam C ++. Hal ini dimaksudkan untuk memungkinkan akuisisi dan pelepasan sumber daya sistem operasi yang aman.

The withPernyataan menciptakan sumber daya dalam lingkup / blok. Anda menulis kode menggunakan sumber daya di dalam blok. Ketika blok keluar sumber daya dilepaskan dengan bersih terlepas dari hasil kode dalam blok (yaitu apakah blok keluar secara normal atau karena pengecualian).

Banyak sumber daya di pustaka Python yang mematuhi protokol yang diperlukan oleh withpernyataan dan dapat digunakan dengan itu out-of-the-box. Namun siapa pun dapat membuat sumber daya yang dapat digunakan dalam pernyataan with dengan mengimplementasikan protokol yang terdokumentasi dengan baik: PEP 0343

Gunakan setiap kali Anda memperoleh sumber daya dalam aplikasi Anda yang harus dilepaskan secara eksplisit seperti file, koneksi jaringan, kunci dan sejenisnya.

Tendayi Mawushe
sumber
27

Sekali lagi untuk kelengkapan, saya akan menambahkan kasus penggunaan saya yang paling berguna untuk withpernyataan.

Saya melakukan banyak komputasi ilmiah dan untuk beberapa kegiatan saya membutuhkan Decimalperpustakaan untuk perhitungan presisi yang sewenang-wenang. Beberapa bagian dari kode saya, saya perlu presisi tinggi dan untuk sebagian besar bagian lain saya perlu kurang presisi.

Saya mengatur presisi default saya ke angka rendah dan kemudian gunakan withuntuk mendapatkan jawaban yang lebih tepat untuk beberapa bagian:

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

Saya sering menggunakan ini dengan Uji Hypergeometrik yang membutuhkan pembagian angka besar yang menghasilkan faktorial. Ketika Anda melakukan perhitungan skala genom, Anda harus berhati-hati terhadap kesalahan pembulatan dan luapan.

Judo Akan
sumber
26

Contoh dari antipattern mungkin menggunakan withloop dalam ketika akan lebih efisien untuk memiliki withloop luar

sebagai contoh

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)

Cara pertama adalah membuka dan menutup file untuk masing-masing rowyang dapat menyebabkan masalah kinerja dibandingkan dengan cara kedua dengan membuka dan menutup file hanya sekali.

John La Rooy
sumber
10

Lihat PEP 343 - Pernyataan 'dengan' , ada bagian contoh di akhir.

... pernyataan baru "with" ke bahasa Python untuk memungkinkan untuk memperhitungkan penggunaan standar dari pernyataan try / akhirnya.

Stefan
sumber
5

poin 1, 2, dan 3 dicakup dengan cukup baik:

4: ini relatif baru, hanya tersedia di python2.6 + (atau python2.5 menggunakan from __future__ import with_statement)

cobbal
sumber
4

Pernyataan with bekerja dengan apa yang disebut manajer konteks:

http://docs.python.org/release/2.5.2/lib/typecontextmanager.html

Idenya adalah untuk menyederhanakan penanganan pengecualian dengan melakukan pembersihan yang diperlukan setelah meninggalkan blok 'with'. Beberapa built-in python sudah berfungsi sebagai manajer konteks.

zefciu
sumber
3

Contoh lain untuk dukungan out-of-the-box, dan yang mungkin agak membingungkan pada awalnya ketika Anda terbiasa dengan cara open()berperilaku built-in , adalah connectionobjek modul database populer seperti:

The connectionobjek adalah manajer konteks dan dengan demikian dapat digunakan out-of-the-box dalam with-statement, namun ketika menggunakan catatan di atas bahwa:

Ketika with-blockselesai, baik dengan pengecualian atau tanpa, koneksi tidak ditutup . Dalam hal with-blockselesai dengan pengecualian, transaksi dibatalkan, jika tidak, transaksi dilakukan.

Ini berarti bahwa programmer harus berhati-hati untuk menutup koneksi sendiri, tetapi memungkinkan untuk memperoleh koneksi, dan menggunakannya secara berganda with-statements, seperti yang ditunjukkan dalam dokumen psycopg2 :

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()

Dalam contoh di atas, Anda akan mencatat bahwa cursorobjek psycopg2juga adalah manajer konteks. Dari dokumentasi yang relevan tentang perilaku:

Ketika cursorkeluar with-blockitu ditutup, melepaskan sumber daya apa pun yang akhirnya dikaitkan dengannya. Keadaan transaksi tidak terpengaruh.

bgse
sumber
3

Dalam python umumnya pernyataan " with " digunakan untuk membuka file, memproses data yang ada dalam file, dan juga untuk menutup file tanpa memanggil metode close (). Pernyataan "dengan" membuat pengecualian penanganan lebih sederhana dengan menyediakan kegiatan pembersihan.

Bentuk umum dengan:

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

catatan: tidak perlu menutup file dengan memanggil close () setelah file-var.close ()

Tushar.PUCSD
sumber