首页 新闻 会员 周边

重复实例化对象 触发__del__方法导致异常

0
悬赏园豆:10 [已解决问题] 解决于 2023-07-22 11:18
import socket

import pymysql


class Mydb(object):
    conn = pymysql.connect(host='location',
                           port=3306,
                           user='root',
                           password='123456',
                           database='socket',
                           charset='utf8'
                           )
    cursor = conn.cursor()
    
    def __init__(self, name):
        self.name = name
        # print(name, '初始化了Mydb类')
    
    # cursor.execute('insert into user(name,adder,age,pass) values (%s,%s,%s,%s)')
    # cursor.close(["张三","上海市浦东新区",20,"123456"])
    def __del__(self):
        print('销毁连接', self.name)
        self.cursor.close()
        self.conn.close()
    
    def query(self, sql) -> str | int | Exception:
        try:
            res = self.cursor.execute(f'{sql}')  # 返回执行sql语句后受影响的行数
        
        except Exception as sql_error:
            return sql_error
        if res >= 1:
            return self.cursor.fetchone()[0]
        else:
            return res
    
    def add(self, sql) -> int | str | Exception:
        """

        :param sql: 更新语句
        :return: 返回1为语句执行成功
        """
        try:
            res = self.cursor.execute(sql)
            self.conn.commit()
        except Exception as update_sql:
            return update_sql
        if res >= 0:
            return 1
        else:
            return f'更新失败: {res}'


class SocketSetting(object):
    
    def __init__(self, name):
        self.db = Mydb(name)
        print(name)
        self.evn: str = 'server'
        self.serverip: str = self.get_server_ip()
        self.clientip: str = self.get_client_ip()
    
    def get_server_ip(self):
        server_ip = socket.gethostbyname(socket.gethostname())
        if self.evn == 'server':
            if self.db.add(f'update socket.ip set server="{server_ip}";') == 1:
                return server_ip
            else:
                return '172.26.120.34'
        else:
            return server_ip
    
    def get_client_ip(self):
        res = self.db.query('select server from socket.ip;')
        print(self.db.name, res)
        if 11 >= len(res) >= 8:
            return res
        else:
            return '172.26.120.34'


class Myoption(object):
    
    def __init__(self, name):
        self.socket = SocketSetting(name)
        

class Run(object):
    
    def __init__(self):
        self.setting = SocketSetting('setting1')
        self.setting = SocketSetting('setting2')
        self.setting = SocketSetting('setting3')

        
        
if __name__ == '__main__':
    run = Run()

上面的代码执行就会报错,我的理解是三个setting实例化的SocketSetting相互独立连接并不会被影响 ,前面的setting被后面的setting对象覆盖会触发__del__方法 但是不知道这里为什么在setting3时报错了
如果按照下面这种来写就不会报错搞不懂为什么

class Run(object):
    
    def __init__(self):
        self.setting = SocketSetting('setting1')
        self.setting = SocketSetting('setting2')
Ttonet的主页 Ttonet | 初学一级 | 园豆:5
提问于:2023-07-13 16:31
< >
分享
最佳答案
1

在你的代码中,出现了 del 方法导致的异常。这是因为 Python 的垃圾回收机制会自动调用 del 方法来进行对象的清理,但是在某些情况下(比如循环引用),del 方法可能会在不同的顺序和时间点被触发,从而导致异常。

在你的第一个示例中,当 Run 类实例化时,会创建三个 SocketSetting 的实例对象 setting1、setting2 和 setting3。然而,后面的两个对象会覆盖前面的对象,导致前面的对象无法被访问并且会被垃圾回收。

当 setting1 对象被垃圾回收时,会触发 Mydb 类的 del 方法,关闭了数据库连接。然后 setting2 对象被创建,同样触发了 Mydb 类的 del 方法,但此时 Mydb 类中的 self.cursor 和 self.conn 已经被关闭,因此会抛出异常。

要解决这个问题,你可以考虑在 SocketSetting 类的 del 方法中添加对 self.db 的判断,以确保在调用 del 方法时,数据库连接是有效的。例如:

python
Copy code
def del(self):
print('销毁连接', self.name)
if self.db:
self.db.cursor.close()
self.db.conn.close()
这样在 setting2 对象被创建时,虽然 setting1 对象已经被覆盖,但 setting1 对象中的数据库连接不会立即关闭,直到垃圾回收时才会关闭。

但是需要注意的是,使用 del 方法来关闭数据库连接并不是一个理想的做法。更好的方式是在代码中显式地关闭数据库连接,或者使用上下文管理器 (with 语句) 来管理数据库连接的生命周期,以确保在使用完毕后及时释放资源。

另外,建议避免在 del 方法中进行复杂的操作,以防止意外发生。如果可能的话,最好在代码中显式地管理对象的生命周期。

收获园豆:5
Technologyforgood | 大侠五级 |园豆:5686 | 2023-07-15 07:37

你好,其实对这个问题我还有个疑问在执行时setting3报错了却不是setting2报错 ,setting2虽然覆盖掉了setting1应该触发__del__方法但是解释器没有立即去执行 是因为当时在执行setting2的初始化代码所以解释器因为全局解释锁的关系无法执行吗

大佬能不能帮忙看下我这个分析对吗:https://www.aliyundrive.com/s/KZBkSzHbzs8

 

 
Ttonet | 园豆:5 (初学一级) | 2023-07-15 15:30
其他回答(1)
1

1.在Mydb类中,在__del__方法中你尝试关闭了数据库连接和游标,但是你的代码中并没有将conn和cursor作为实例属性进行保存,因此在__del__方法中无法访问这些属性。你可以考虑在__init__方法中将它们保存为实例属性,以便在__del__方法中使用。

2.在SocketSetting类中,你在__init__方法中实例化了一个Mydb对象,但是在后续的代码中并没有使用到这个对象。如果你想在SocketSetting类中使用数据库连接,你需要将db对象作为一个实例属性保存,并在需要的地方使用它。

3.在Run类中,你实例化了多个SocketSetting对象,但是每次实例化都将前一个对象覆盖掉了。因此,在你的代码中只会保留最后一个SocketSetting对象的引用。如果你想在Run类中使用多个SocketSetting对象,可以考虑使用列表或字典等数据结构来保存这些对象。

调整后示例代码,仅供参考:

import pymysql
import socket

class Mydb(object):
    def __init__(self, name):
        self.name = name
        self.conn = pymysql.connect(host='location', port=3306, user='root', password='123456', database='socket', charset='utf8')
        self.cursor = self.conn.cursor()
    
    def __del__(self):
        print('销毁连接', self.name)
        self.cursor.close()
        self.conn.close()
    
    def query(self, sql) -> str|int|Exception:
        try:
            res = self.cursor.execute(sql)
            if res >= 1:
                return self.cursor.fetchone()[0]
            else:
                return res
        except Exception as sql_error:
            return sql_error
    
    def add(self, sql) -> int|str|Exception:
        try:
            res = self.cursor.execute(sql)
            self.conn.commit()
            if res >= 0:
                return 1
            else:
                return f'更新失败:{res}'
        except Exception as update_sql:
            return update_sql

class SocketSetting(object):
    def __init__(self, name):
        self.name = name
        self.db = Mydb(name)
        print(name)
        self.evn:str = 'server'
        self.serverip:str = self.get_server_ip()
        self.clientip:str = self.get_client_ip()
    
    def get_server_ip(self):
        server_ip = socket.gethostbyname(socket.gethostname())
        if self.evn == 'server':
            if self.db.add(f'update socket.ip set server="{server_ip}";') == 1:
                return server_ip
            else:
                return '172.26.120.34'
        else:
            return server_ip
    
    def get_client_ip(self):
        res = self.db.query('select server from socket.ip;')
        print(self.db.name, res)
        if 11 >= len(res) >= 8:
            return res
        else:
            return '172.26.120.34'

class Myoption(object):
    def __init__(self, name):
        self.socket = SocketSetting(name)

class Run(object):
    def __init__(self):
        self.settings = []
        self.settings.append(SocketSetting('setting1'))
        self.settings.append(SocketSetting('setting2'))
        self.settings.append(SocketSetting('setting3'))
收获园豆:5
lanedm | 园豆:2378 (老鸟四级) | 2023-07-14 11:38

大佬能不能帮忙看下我这个解析对吗:https://www.aliyundrive.com/s/KZBkSzHbzs8

支持(0) 反对(0) Ttonet | 园豆:5 (初学一级) | 2023-07-14 11:42
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册