我真的被上下文管理器 (__enter__ & __exit__) 和 mysql.connector 困住了,但有一个错误

I really got stuck about context managers (__enter__ & __exit__) and mysql.connector with an error

我写了一个 class 来从 MySQL 数据库收集数据。 当我使用 with.

时,我不明白如何使用 __enter____exit__ 方法

更具体的我没看懂:

  1. 我应该在哪里 return self.raws = self.crouser.execute(self.que) 实际上是数据库中的数据。

  2. 连接打开到什么时候?

  3. 如何在这个具体问题中使用with方法?

我的代码:

import mysql.connector

class tak:
     def __init__(self,host1, user1, password1, db1,aat, que1):
          self.host = host1
          self.user = user1
          self.password = password1
          self.database = db1
          self.auth_plugin = aat
          self.que = que1

     def co(self):
          try:
               self.my = mysql.connector.connect(
                    host = self.host,
                    user = self.user,
                    password = self.password,
                    database = self.database,
                    auth_plugin = self.auth_plugin,
                    charset = 'utf8'
               )
          except mysql.connector.errors as err :
               print(f'{err}')
          self.crouser = self.my.cursor()
          self.raws = self.crouser.execute(self.que)
          self.crouser.fetchall()

     def __enter__(self):
          return

     def __exit__(self, exc_type, exc_val, exc_tb):
          self


with tak(host1="localhost",
         user1="root",
         password1="1234",
         db1="local_db",
         aat='mysql_native_password',
         que1='select * from nand').co() as log:
     print(log)

您应该在 __enter__ 方法中执行所有初始化并使它 return 上下文管理器实例,并且您应该在 __exit__ 方法中执行清理:

class tak:
     def __init__(self,host1, user1, password1, db1,aat, que1):
          self.host = host1
          self.user = user1
          self.password = password1
          self.database = db1
          self.auth_plugin = aat
          self.que = que1

     def __enter__(self):
          try:
               self.my = mysql.connector.connect(
                    host = self.host,
                    user = self.user,
                    password = self.password,
                    database = self.database,
                    auth_plugin = self.auth_plugin,
                    charset = 'utf8'
               )
          except mysql.connector.errors as err :
               print(f'{err}')
          self.crouser = self.my.cursor()
          self.raws = self.crouser.execute(self.que)
          self.crouser.fetchall()              
          return self
     
     def __exit__(self, exc_type, exc_val, exc_tb):
          self.my.close()

以便您可以直接将其实例化为上下文管理器:

with tak(host1="localhost",
          user1="root",
          password1="1234",
          db1="local_db",
          aat='mysql_native_password',
          que1='select * from nand') as log:
     # do something with log here

下面大量解释:

__enter__ 准备东西(打开应该稍后关闭的东西等)。如果你想做with ... as ...,那么返回的对象应该是你想在as ...中得到的对象(通常是self,但不一定是)。

__exit__ 应该清理东西 - 关闭应该关闭的东西,重新分配它们的原始值...

我不知道您为什么已经在构造函数中执行查询。 经典方式是:__enter__ 打开连接,__exit__ 关闭。然后任何查询都在块中。

与文件操作比较:

f = open(filename)
data = f.readlines()
f.close()

变为:

with open(filename) as f: # __enter__ returns self
    data = f.readlines()
# we exit the block, so __exit__ is performed - it does f.close() for us

虽然您的代码更像...

with open_and_read(filename) as data:
    # ????? do something with data I guess?

你就是这样做的:打开东西并做一件预定义的事情......并获得不可关闭的东西,这样它就可以活得更久(现在它只存在于 with 块中)。

如果这真的是你想要的,那么完全放弃上下文管理器:只做一个打开连接、获取查询、关闭连接的函数,returnslog

但是如果你想要一个正确的上下文管理器,它可能看起来像这样:

import mysql.connector
class tak :
     def __init__(self,host1, user1, password1, db1,aat): #removed query
          self.host = host1
          self.user = user1
          self.password = password1
          self.database = db1
          self.auth_plugin = aat
          # self.que = que1 nope, we don't want it here

     def __enter__(self):
          self.my = mysql.connector.connect(
              host = self.host,
              user = self.user,
              password = self.password,
              database = self.database,
              auth_plugin = self.auth_plugin,
              charset = 'utf8'
          )
          return self.my.cursor()
     
     def __exit__(self, exc_type, exc_val, exc_tb):
          self.my.close()
     

# now we can get database info once but open the connection multiple times
db = tak(...)
with db as cur: # our __enter__ returns the cursor
    cur.execute(...)
    data1 = cur.fetchall()
# as we exit the block, __exit__ is called

# do something with data1

with db as cur: # open new connection, return new cursor
    cur.execute(...)
    ...
...

[我还没有测试这段代码,它可能需要一些调整。我回答的重点是解释上下文管理器的正确用法,而不是给出完美的代码。]