【问题标题】:I really got stuck about context managers (__enter__ & __exit__) and mysql.connector with an error我真的对上下文管理器(__enter__ & __exit__)和 mysql.connector 有一个错误感到困惑
【发布时间】:2021-11-04 02:58:51
【问题描述】:

我写了一个类来从 MySQL 数据库中收集数据。 当我使用with 时,我不明白如何使用__enter____exit__ 方法。

更具体地说,我不明白:

  1. 我应该在哪里返回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)

【问题讨论】:

    标签: python mysql-python with-statement python-class


    【解决方案1】:

    下面有很多解释:

    __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 块内)。

    如果这确实是您想要的,那么完全放弃上下文管理器:只需执行一个打开连接、获取查询、关闭连接、返回 log 的函数。

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

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

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

    【讨论】:

    • @ZurHanin 如果答案对您有用,请接受。
    【解决方案2】:

    您应该在__enter__ 方法中执行所有初始化并使其返回上下文管理器实例,您应该在__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
    

    【讨论】:

    • 嘿,谢谢,但是当我运行它并打印结果时:with tak(host1="localhost", user1="root", password1="1234", db1="local_db", aat= 'mysql_native_password', que1='select * from nand') as log: print(log) 我得到输出:无
    • 你确定你在__enter__方法中有return self吗?听起来好像您没有return 声明,就像您没有在问题中一样。
    猜你喜欢
    • 2019-04-27
    • 1970-01-01
    • 2022-10-31
    • 2012-03-04
    • 2016-02-09
    • 1970-01-01
    • 2022-06-13
    • 2017-04-21
    • 2012-09-21
    相关资源
    最近更新 更多