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, with
anahtar 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 with
halihazı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 with
bloktan __exit__
çıkıldığında yürütülür with
. Daha sonra bu nesneye başvurmayı düşünüyorsanız, with EXPR as VAR
tarafı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, with
bloktan çı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ü with
yalnızca bağlantıyı yönetmektedir. Bu nedenle, with
bloktan çı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, MySQLdb
herhangi 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.BaseCursor
sınıf doğrudan object
teslim 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.Connection
sınıfın __enter__
ve __exit__
yöntemlerin size neden her with
blokta 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 with
bloktan çı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ı close
sonunda yöntemini with
bloğu. (Sadece bir sınıf tanımlamak, Bu eylemi görmek için Foo
ile __enter__
, __exit__
ve close
yöntemler basit içeren print
ifadeleri ve siz ne olacağını karşılaştırmak with Foo(): pass
bunu 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 BEGIN
kullandığınızda with connection
ve 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 BEGIN
ve sunucudaki otomatik tamamlama ayarını atlamak için açık olanı kullanır . Kullanmaya with connection
alış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şabilirsinclosing
kodunuza 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 VAR
bağlanan bağlantı nesnesini için VAR
aksine, with MySQLdb.connect(user, pass) as VAR
bağlar, yeni imleç nesnesi için VAR
. İkinci durumda, bağlantı nesnesine doğrudan erişiminiz olmazdı! Bunun yerine, connection
orijinal bağlantıya proxy erişimi sağlayan imlecin özniteliğini kullanmanız gerekir . İmleç kapatıldığında, connection
niteliğ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