Standart uygulamanın ne olduğunu sormak yerine, bu genellikle belirsiz ve öznel olduğundan, rehberlik için modülün kendisine bakmayı deneyebilirsiniz. Genel olarak, withanahtar kelimeyi başka bir kullanıcının önerdiği gibi kullanmak harika bir fikirdir, ancak bu özel durumda size beklediğiniz işlevselliği tam olarak vermeyebilir.
Modülün 1.2.5 sürümünden itibaren MySQLdb.Connection, bağlam yöneticisi protokolünü aşağıdaki kodla ( github ) uygular :
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()
Hakkında withhalihazırda var olan birkaç Soru-Cevap var veya Python'un "with" ifadesini Anlama'yı okuyabilirsiniz , ancak esasen olan şey bloğun __enter__başında çalıştırılır ve withbloktan __exit__çıkıldığında yürütülür with. Daha sonra bu nesneye başvurmayı düşünüyorsanız, with EXPR as VARtarafından döndürülen nesneyi __enter__bir ada bağlamak için isteğe bağlı sözdizimini kullanabilirsiniz . Dolayısıyla, yukarıdaki uygulama göz önüne alındığında, veritabanınızı sorgulamanın basit bir yolu:
connection = MySQLdb.connect(...)
with connection as cursor:
cursor.execute('select 1;')
result = cursor.fetchall()
print result
Şimdi soru, withbloktan çıktıktan sonra bağlantının ve imlecin durumları nelerdir? __exit__Sadece aramalar yukarıda gösterilen yöntem self.rollback()veya self.commit()ve ne bu yöntemlerin çağırmak için devam close()yöntemi. İmlecin kendisinin __exit__tanımlanmış bir yöntemi yoktur ve tanımlanmış olması önemli değildir, çünkü withyalnızca bağlantıyı yönetmektedir. Bu nedenle, withbloktan çıktıktan sonra hem bağlantı hem de imleç açık kalır . Bu, yukarıdaki örneğe aşağıdaki kodu ekleyerek kolayca onaylanabilir:
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'
Stdout'a yazdırılan "imleç açık; bağlantı açık" çıktısını görmelisiniz.
Bağlantıyı kurmadan önce imleci kapatmanız gerektiğine inanıyorum.
Neden? MySQL Cı API temelidir, MySQLdbherhangi bir imleç nesnesi uygulamaz, modül belgelerinde ima edilmiştir: "MySQL imleçler desteklemez, ancak imleçler kolayca taklit vardır." Aslında, MySQLdb.cursors.BaseCursorsınıf doğrudan objectteslim etme / geri alma ile ilgili olarak imleçlerden miras alır ve bu tür bir kısıtlama uygulamaz. Bir Oracle geliştiricisi şunu söylemişti :
cnx.commit () cur.close () 'dan önce bana en mantıklı geliyor. Belki şu kurala göre gidebilirsiniz: "Artık ihtiyacınız yoksa imleci kapatın." Böylece imleci kapatmadan önce commit (). Sonunda, Connector / Python için çok fazla fark yaratmaz, ancak veya diğer veritabanları için olabilir.
Bu konuda "standart uygulamaya" en yakın olacağınızı umuyorum.
Her işlem için yeni imleçler almak zorunda kalmamak için ara taahhüt gerektirmeyen işlem kümelerini bulmanın önemli bir avantajı var mı?
Bundan çok şüpheliyim ve bunu yapmaya çalışırken, ek insan hatası da ortaya çıkabilir. Bir kongreye karar vermek ve ona bağlı kalmak daha iyidir.
Yeni imleçler elde etmenin çok fazla ek yükü var mı, yoksa bu önemli bir şey değil mi?
Ek yük ihmal edilebilir düzeydedir ve veritabanı sunucusuna hiç dokunmaz; tamamen MySQLdb'nin uygulanması içindedir. Sen olabilir bakmak BaseCursor.__init__github size yeni imleç oluştururken neler olduğunu bilmek gerçekten merak eğer.
Daha önce tartıştığımız zamana geri dönersek with, belki şimdi MySQLdb.Connectionsınıfın __enter__ve __exit__yöntemlerin size neden her withblokta yepyeni bir imleç nesnesi verdiğini ve onu takip etmek veya bloğun sonunda kapatmakla uğraşmadığını anlayabilirsiniz . Oldukça hafif ve tamamen sizin rahatınız için var.
İmleç nesnesinin mikro yönetimini yapmak sizin için gerçekten bu kadar önemliyse , imleç nesnesinin tanımlanmış bir yöntemi olmadığı gerçeğini telafi etmek için contextlib.closing'i kullanabilirsiniz __exit__. Bu nedenle, bağlantı nesnesini bir withbloktan çıktıktan sonra kendini kapatmaya zorlamak için de kullanabilirsiniz . Bu, "my_curs kapalı; my_conn kapalı" çıktısını almalıdır:
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'
Not with closing(arg_obj)argüman nesne en aramayacağım __enter__ve __exit__yöntemler; o olacak sadece argüman nesnenin çağrı closesonunda yöntemini withbloğu. (Sadece bir sınıf tanımlamak, Bu eylemi görmek için Fooile __enter__, __exit__ve closeyöntemler basit içeren printifadeleri ve siz ne olacağını karşılaştırmak with Foo(): passbunu yaptığında olanlara with closing(Foo()): pass.) Bu iki önemli etkileri vardır:
İlk olarak, otomatik yürütme modu etkinleştirilmişse, MySQLdb , bloğun sonunda işlemi BEGINkullandığınızda with connectionve gerçekleştirdiğinizde veya geri aldığınızda sunucuda açık bir işlem yapar . Bunlar MySQLdb'nin varsayılan davranışlarıdır ve sizi MySQL'in her türlü DML ifadesini hemen işleme koyma şeklindeki varsayılan davranışından korumayı amaçlamaktadır. MySQLdb, bir bağlam yöneticisi kullandığınızda, bir işlem istediğinizi varsayar BEGINve sunucudaki otomatik tamamlama ayarını atlamak için açık olanı kullanır . Kullanmaya with connectionalışkınsanız, aslında yalnızca atlandığında otomatik tamamlamanın devre dışı bırakıldığını düşünebilirsiniz. Eklersen hoş olmayan bir sürprizle karşılaşabilirsinclosingkodunuza ve işlem bütünlüğünü kaybetmenize; değişiklikleri geri alamazsınız, eşzamanlılık hataları görmeye başlayabilirsiniz ve bunun nedeni hemen belli olmayabilir.
İkincisi, with closing(MySQLdb.connect(user, pass)) as VARbağlanan bağlantı nesnesini için VARaksine, with MySQLdb.connect(user, pass) as VARbağlar, yeni imleç nesnesi için VAR. İkinci durumda, bağlantı nesnesine doğrudan erişiminiz olmazdı! Bunun yerine, connectionorijinal bağlantıya proxy erişimi sağlayan imlecin özniteliğini kullanmanız gerekir . İmleç kapatıldığında, connectionniteliği olarak ayarlanır None. Bu, aşağıdakilerden biri olana kadar devam eden terk edilmiş bir bağlantıyla sonuçlanır:
- İmlecin tüm referansları kaldırılır
- İmleç kapsam dışına çıkıyor
- Bağlantı zaman aşımına uğradı
- Bağlantı, sunucu yönetim araçları aracılığıyla manuel olarak kapatılır
Bunu , aşağıdaki satırları tek tek çalıştırırken açık bağlantıları izleyerek (Workbench'te veya kullanarakSHOW PROCESSLIST ) test edebilirsiniz :
with MySQLdb.connect(...) as my_curs:
pass
my_curs.close()
my_curs.connection
my_curs.connection.close()
del my_curs