鱼C论坛

 找回密码
 立即注册
查看: 2153|回复: 11

函数内报错后局部变量不释放问题

[复制链接]
发表于 2022-4-7 14:40:43 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
如果在函数内创建数据库连接, 在后续执行其他语句时报错引发异常退出, 那么这个连接将不会被关闭, 如何能获取到这个局部的数据库连接并将它关闭呢

虽然应该在函数内部加入try来捕获异常, 加入finally来处理关闭, 或是用with语句, 但是上述问题如果存在的话应该怎么处理呢

在再次对函数进行赋值或删除函数后, 数据库连接关闭了, 那么意味着这个连接是跟函数进行绑定的, 怎么通过这个函数来获取到这个连接?
如:
import psycopg2

def f():
    conn = psycopg2.connect(host='xxx', user='xxx', password='xxx')
    cursor = conn.cursor()
    cursor.execute('select select')
    return
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2022-4-7 16:47:11 | 显示全部楼层
ba21 发表于 2022-4-7 16:14
就算不想也没别的办法。
你能把全部相关代码写在同一作用域。
或者变向 lst = [conn ] 存入列表中。把 ...

我如果在函数中输出id(conn), 在报错之后是可以通过内存地址取到这个连接变量的, 问题在于函数内部的局部变量在函数报错后不释放, 就算不是数据库连接, 而是一个列表或字典也是一样的
In [9]: def g():
   ...:     conn = [1, 2, 3]
   ...:     print(id(conn))
   ...:     cursor = conn.cursor()
   ...:     cursor.execute('select select')
   ...:     conn.close()
   ...:     return
   ...: 

In [10]: g()
140065573745480
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-10-5fd69ddb5074> in <module>
----> 1 g()

<ipython-input-9-59a7e29f8a3c> in g()
      2     conn = [1, 2, 3]
      3     print(id(conn))
----> 4     cursor = conn.cursor()
      5     cursor.execute('select select')
      6     conn.close()

AttributeError: 'list' object has no attribute 'cursor'

In [11]: get_value = ctypes.cast(140065573745480, ctypes.py_object).value

In [12]: get_value
Out[12]: [1, 2, 3]

In [13]: def h():
    ...:     conn = [1, 2, 3]
    ...:     print(id(conn))
    ...: 

In [14]: h()
140065573750024

In [15]: get_value = ctypes.cast(140065573750024, ctypes.py_object).value

In [16]: get_value
Out[16]: (0, 37)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-4-7 15:45:05 | 显示全部楼层
import psycopg2
conn = None # 可变对象只能使用全局变量
def f():
   global conn
    conn = psycopg2.connect(host='xxx', user='xxx', password='xxx')
    cursor = conn.cursor()
    cursor.execute('select select')
    return
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-4-7 16:02:27 | 显示全部楼层
ba21 发表于 2022-4-7 15:45
import psycopg2
conn = None # 可变对象只能使用全局变量
def f():

但是我并不想污染全局变量, 只是想把函数中报错产生的残留连接关闭
用类似闭包的方法__closure__获取不到
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-4-7 16:14:43 | 显示全部楼层
DetConan 发表于 2022-4-7 16:02
但是我并不想污染全局变量, 只是想把函数中报错产生的残留连接关闭
用类似闭包的方法__closure__获取不 ...

就算不想也没别的办法。
你能把全部相关代码写在同一作用域。
或者变向 lst = [conn ] 存入列表中。把列表当参数传递,调用同样使用列表来调用
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-4-7 16:44:12 | 显示全部楼层
本帖最后由 阿奇_o 于 2022-4-7 16:55 编辑

函数的函数体就是个黑盒子,你是不能读取里面的局部变量的, 所以 除了改为全局变量,还有下面两三种方法:

1. 返回conn对象,这样外部就可以获取

2. 把函数逻辑 改写为 类,类实例里可以将连接对象保存为一个实例变量,如 mc = MyConnection(); mc.conn = psycopg2.connect(...)
    或尝试改写为 "描述器"(绑定到类变量上),这样就看你具体需要了。

3. 将连接对象放到容器里,并作为函数的参数,函数体内就可以通过该容器找到需要的连接对象进行操作,类似 操作"连接池", 如
conn_pool = [conn1, conn2, ...]    # 这只是示例。经典的处理,我记得好像bottle和flask的是采用一个 栈类实现的,需要一个就生产一个连接。
def getConn(conns = conn_pool):      # 这其实相当于改为全局变量的情况
      cursor = conns[0].cursor()
      cursor.execute('select select')   


补充: 至于报错,可能还要看这个模块psycopg2它在处理异常时,是否进行了抛出,它如果没抛出异常,你函数也不会报异常。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-4-7 16:54:18 | 显示全部楼层
阿奇_o 发表于 2022-4-7 16:44
函数的函数体就是个黑盒子,你是不能读取里面的局部变量的, 所以 除了改为全局变量,还有下面两三种方法: ...

这个我能明白, 不仅是数据库连接的情况, 其他的函数都存在这样的问题, 函数报错后局部变量不释放内存, 在报错后执行gc.collect()也不能回收, 不知道是我的版本问题还是所有版本都有这个问题python3.6.12
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-4-7 16:55:43 | 显示全部楼层
而且正因为能通过内存地址取到正确的变量, 我才觉得应该有某种方法能正常获取到这个变量的引用, 不然当引用计数为0时就应该跟不报错时的行为一致, 被gc回收掉了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-4-7 17:11:14 | 显示全部楼层
你说,"函数报错后局部变量不释放内存, 在报错后执行gc.collect()也不能回收", 这。。

疑问一,难道是 conn对象在函数里被创建后,就被固定了(已加载的函数局部变量)。。?
嗯,我想应该是 局部变量应该是和这个函数一起绑定的,函数还在(已定义了,存在于命名空间),那么它里面的局部变量也存在着(只是不能从外部获取和修改)。

疑问二,为啥你要手动 gc.collect() ?? 我是从来没用过这。。

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-4-7 17:32:05 | 显示全部楼层
阿奇_o 发表于 2022-4-7 17:11
你说,"函数报错后局部变量不释放内存, 在报错后执行gc.collect()也不能回收", 这。。

疑问一,难道是  ...

对于疑问一: 确实感觉上是跟函数绑定的, 如果重新定义了函数或删除了函数, 也就释放了里面的局部变量

对于疑问二: 我是感觉自动的垃圾回收可能出问题了, 所以才尝试手动执行, 但实际上垃圾回收并不会处理这部分内容, 可能就像是函数的某个属性一样, 引用计数还在
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-4-7 17:35:05 | 显示全部楼层
补充:要拿pg连接的id,可以用 conn.xid(1, "gtrid", "bqual")   我特定查了一下。

其他你自己琢磨吧,很多事语言文字是难以说明白的   ^_
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-4-8 16:49:34 | 显示全部楼层
请看置顶帖中的不释放内存问题~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-10-7 06:41

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表