Daripada menanyakan apa itu praktik standar, karena sering kali tidak jelas dan subjektif, Anda dapat mencoba melihat modul itu sendiri sebagai panduan. Secara umum, menggunakan with
kata kunci seperti yang disarankan pengguna lain adalah ide bagus, tetapi dalam keadaan khusus ini mungkin tidak memberikan fungsionalitas yang Anda harapkan.
Pada versi 1.2.5 modul, MySQLdb.Connection
mengimplementasikan protokol manajer konteks dengan kode berikut ( github ):
def __enter__(self):
if self.get_autocommit():
self.query("BEGIN")
return self.cursor()
def __exit__(self, exc, value, tb):
if exc:
self.rollback()
else:
self.commit()
Ada beberapa Tanya Jawab yang with
sudah ada, atau Anda dapat membaca Memahami pernyataan "dengan" Python , tetapi pada dasarnya apa yang terjadi adalah yang __enter__
mengeksekusi di awal with
blok, dan __exit__
mengeksekusi setelah meninggalkan with
blok. Anda dapat menggunakan sintaks opsional with EXPR as VAR
untuk mengikat objek yang dikembalikan oleh __enter__
nama jika Anda bermaksud untuk mereferensikan objek itu nanti. Jadi, dengan penerapan di atas, berikut adalah cara sederhana untuk membuat kueri database Anda:
connection = MySQLdb.connect(...)
with connection as cursor:
cursor.execute('select 1;')
result = cursor.fetchall()
print result
Pertanyaannya sekarang adalah, apa status koneksi dan kursor setelah keluar dari with
blok? The __exit__
Metode yang ditunjukkan di atas panggilan hanya self.rollback()
atau self.commit()
, dan tak satu pun dari metode tersebut pergi untuk memanggil close()
metode. Kursor itu sendiri tidak memiliki __exit__
metode yang ditentukan - dan tidak masalah jika demikian, karena with
hanya mengelola koneksi. Oleh karena itu, koneksi dan kursor tetap terbuka setelah keluar dari with
blokir. Ini mudah dikonfirmasi dengan menambahkan kode berikut ke contoh di atas:
try:
cursor.execute('select 1;')
print 'cursor is open;',
except MySQLdb.ProgrammingError:
print 'cursor is closed;',
if connection.open:
print 'connection is open'
else:
print 'connection is closed'
Anda akan melihat output "kursor terbuka; koneksi terbuka" dicetak ke stdout.
Saya yakin Anda perlu menutup kursor sebelum melakukan koneksi.
Mengapa? The MySQL C API , yang merupakan dasar untuk MySQLdb
, tidak mengimplementasikan objek kursor, seperti yang tersirat dalam dokumentasi modul: "MySQL tidak mendukung kursor, namun kursor yang mudah ditiru." Memang, MySQLdb.cursors.BaseCursor
kelas tersebut mewarisi langsung dari object
dan tidak memberlakukan pembatasan seperti itu pada kursor terkait dengan commit / rollback. Pengembang Oracle mengatakan ini :
cnx.commit () sebelum cur.close () terdengar paling logis bagi saya. Mungkin Anda dapat mengikuti aturan: "Tutup kursor jika Anda tidak membutuhkannya lagi." Jadi komit () sebelum menutup kursor. Pada akhirnya, untuk Connector / Python, itu tidak membuat banyak perbedaan, tetapi mungkin untuk database lain.
Saya berharap itu sedekat Anda dengan "praktik standar" tentang subjek ini.
Adakah keuntungan yang signifikan untuk menemukan rangkaian transaksi yang tidak memerlukan komitmen perantara sehingga Anda tidak perlu mendapatkan kursor baru untuk setiap transaksi?
Saya sangat meragukannya, dan saat mencoba melakukannya, Anda mungkin menyebabkan kesalahan manusia tambahan. Lebih baik memutuskan konvensi dan mematuhinya.
Apakah ada banyak biaya tambahan untuk mendapatkan kursor baru, atau itu bukan masalah besar?
Overhead dapat diabaikan, dan tidak menyentuh server database sama sekali; itu sepenuhnya dalam implementasi MySQLdb. Anda dapat melihat di BaseCursor.__init__
github jika Anda benar-benar ingin tahu apa yang terjadi saat Anda membuat kursor baru.
Kembali ke awal ketika kita berdiskusi with
, mungkin sekarang Anda dapat memahami mengapa MySQLdb.Connection
kelas __enter__
dan __exit__
metode memberi Anda objek kursor baru di setiap with
blok dan tidak repot-repot melacak atau menutupnya di akhir blok. Ini cukup ringan dan hanya ada untuk kenyamanan Anda.
Jika benar-benar penting bagi Anda untuk mengatur mikro objek kursor, Anda dapat menggunakan contextlib.closing untuk menggantikan fakta bahwa objek kursor tidak memiliki __exit__
metode yang ditentukan . Dalam hal ini, Anda juga dapat menggunakannya untuk memaksa objek koneksi menutup sendiri saat keluar dari with
blok. Ini harus menghasilkan "my_curs ditutup; my_conn ditutup":
from contextlib import closing
import MySQLdb
with closing(MySQLdb.connect(...)) as my_conn:
with closing(my_conn.cursor()) as my_curs:
my_curs.execute('select 1;')
result = my_curs.fetchall()
try:
my_curs.execute('select 1;')
print 'my_curs is open;',
except MySQLdb.ProgrammingError:
print 'my_curs is closed;',
if my_conn.open:
print 'my_conn is open'
else:
print 'my_conn is closed'
Perhatikan bahwa with closing(arg_obj)
tidak akan memanggil objek argumen __enter__
dan __exit__
metode; itu hanya akan memanggil metode objek argumen close
di akhir with
blok. (Untuk melihat ini beraksi, cukup tentukan kelas Foo
dengan __enter__
,, __exit__
dan close
metode yang berisi print
pernyataan sederhana , dan bandingkan apa yang terjadi ketika Anda melakukan with Foo(): pass
apa yang terjadi ketika Anda melakukannya with closing(Foo()): pass
.) Ini memiliki dua implikasi signifikan:
Pertama, jika mode autocommit diaktifkan, MySQLdb akan BEGIN
melakukan transaksi eksplisit di server saat Anda menggunakan with connection
dan melakukan atau mengembalikan transaksi di akhir blok. Ini adalah perilaku default MySQLdb, yang dimaksudkan untuk melindungi Anda dari perilaku default MySQL yang segera melakukan setiap dan semua pernyataan DML. MySQLdb mengasumsikan bahwa ketika Anda menggunakan manajer konteks, Anda menginginkan transaksi, dan menggunakan eksplisit BEGIN
untuk melewati pengaturan autocommit di server. Jika Anda terbiasa menggunakan with connection
, Anda mungkin mengira autocommit dinonaktifkan padahal sebenarnya hanya dilewati. Anda mungkin mendapatkan kejutan yang tidak menyenangkan jika menambahkanclosing
ke kode Anda dan kehilangan integritas transaksional; Anda tidak akan dapat membatalkan perubahan, Anda mungkin mulai melihat bug konkurensi dan alasannya mungkin tidak langsung diketahui.
Kedua, with closing(MySQLdb.connect(user, pass)) as VAR
mengikat objek koneksi ke VAR
, berbeda dengan with MySQLdb.connect(user, pass) as VAR
, yang mengikat objek kursor baru ke VAR
. Dalam kasus terakhir, Anda tidak akan memiliki akses langsung ke objek koneksi! Sebagai gantinya, Anda harus menggunakan connection
atribut kursor , yang menyediakan akses proxy ke koneksi asli. Saat kursor ditutup, connection
atributnya disetel ke None
. Ini menghasilkan koneksi yang ditinggalkan yang akan bertahan sampai salah satu hal berikut terjadi:
- Semua referensi ke kursor dihapus
- Kursor keluar dari ruang lingkup
- Waktu koneksi habis
- Koneksi ditutup secara manual melalui alat administrasi server
Anda dapat mengujinya dengan memantau koneksi terbuka (di Workbench atau dengan menggunakanSHOW PROCESSLIST
) saat menjalankan baris berikut satu per satu:
with MySQLdb.connect(...) as my_curs:
pass
my_curs.close()
my_curs.connection
my_curs.connection.close()
del my_curs