DetConan 发表于 2022-4-7 14:40:43

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

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

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

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

def f():
    conn = psycopg2.connect(host='xxx', user='xxx', password='xxx')
    cursor = conn.cursor()
    cursor.execute('select select')
    return

DetConan 发表于 2022-4-7 16:47:11

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

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

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

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

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

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

In : get_value
Out:

In : def h():
    ...:   conn =
    ...:   print(id(conn))
    ...:

In : h()
140065573750024

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

In : get_value
Out: (0, 37)

ba21 发表于 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

DetConan 发表于 2022-4-7 16:02:27

ba21 发表于 2022-4-7 15:45
import psycopg2
conn = None # 可变对象只能使用全局变量
def f():


但是我并不想污染全局变量, 只是想把函数中报错产生的残留连接关闭
用类似闭包的方法__closure__获取不到

ba21 发表于 2022-4-7 16:14:43

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

就算不想也没别的办法。
你能把全部相关代码写在同一作用域。
或者变向 lst = 存入列表中。把列表当参数传递,调用同样使用列表来调用

阿奇_o 发表于 2022-4-7 16:44:12

本帖最后由 阿奇_o 于 2022-4-7 16:55 编辑

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

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

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

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


补充: 至于报错,可能还要看这个模块psycopg2它在处理异常时,是否进行了抛出,它如果没抛出异常,你函数也不会报异常。

DetConan 发表于 2022-4-7 16:54:18

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

这个我能明白, 不仅是数据库连接的情况, 其他的函数都存在这样的问题, 函数报错后局部变量不释放内存, 在报错后执行gc.collect()也不能回收, 不知道是我的版本问题还是所有版本都有这个问题python3.6.12

DetConan 发表于 2022-4-7 16:55:43

而且正因为能通过内存地址取到正确的变量, 我才觉得应该有某种方法能正常获取到这个变量的引用, 不然当引用计数为0时就应该跟不报错时的行为一致, 被gc回收掉了

阿奇_o 发表于 2022-4-7 17:11:14

你说,"函数报错后局部变量不释放内存, 在报错后执行gc.collect()也不能回收", 这。。

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

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

DetConan 发表于 2022-4-7 17:32:05

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

疑问一,难道是...

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

对于疑问二: 我是感觉自动的垃圾回收可能出问题了, 所以才尝试手动执行, 但实际上垃圾回收并不会处理这部分内容, 可能就像是函数的某个属性一样, 引用计数还在

阿奇_o 发表于 2022-4-7 17:35:05

补充:要拿pg连接的id,可以用 conn.xid(1, "gtrid", "bqual")   我特定查了一下。

其他你自己琢磨吧,很多事语言文字是难以说明白的   ^_

DetConan 发表于 2022-4-8 16:49:34

请看置顶帖中的不释放内存问题~
页: [1]
查看完整版本: 函数内报错后局部变量不释放问题