Python对象生命周期怪异问题。

hxDreamer 2010-06-17 12:11:37
哎 又遇到不解的问题了,初学python。

class Person:
count=0

def __init__(self,name):
self.name=name
self.myname=name # 必须指明self.对象变量名,否则视为函数局部变量
Person.count+=1 # 必须用类名.类变量名
print 'Init! name=%s, count=' % name,Person.count

def __del__(self):
Person.count-=1
print 'Del name=%s, count=' % self.myname,Person.count


swaroop = Person('Swaroop')
swaroo1p = Person('Swaro2op') # swaroo2p不出错,swaroo1p出错。郁闷啊kalam = Person('Abdul Kalam')

这个代码的输出为:
Init! name=Swaroop, count= 1
Init! name=Swaro2op, count= 2
Init! name=Abdul Kalam, count= 3
Del name=Abdul Kalam, count= 2
Del name=Swaroop, count= 1
Exception AttributeError: "'NoneType' object has no attribute 'count'" in <bound method Person.__del__ of <__main__.Person instance at 0x017008A0>> ignored

我不理解为什么最后一行(最后一个对象析构会出错)

更怪异的是,吧代码的最后3行改为:
swaroop = Person('Swaroop')
swaroo2p = Person('Swaro2op') # swaroo2p不出错,swaroo1p出错。郁闷啊kalam = Person('Abdul Kalam')

结果貌似正确了:
Init! name=Swaroop, count= 1
Init! name=Swaro2op, count= 2
Init! name=Abdul Kalam, count= 3
Del name=Swaro2op, count= 2
Del name=Abdul Kalam, count= 1
Del name=Swaroop, count= 0

回帖即给分,能给出解释的给大部分分。先谢谢大家了。
...全文
431 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
thy38 2010-06-20
  • 打赏
  • 举报
回复
__del__最好避免用
hxDreamer 2010-06-17
  • 打赏
  • 举报
回复
对了 我补充一下。我是看着《简明 Python 教程》第11章写的。以前学vb6,c#,vb.net,java,c++,asm都没这么郁闷过。
thy38 2010-06-17
  • 打赏
  • 举报
回复
首先LZ要明白一点,Python的内存管理是基于垃圾回收器机制的,所以并不像C++那样在超出对象作用域的时候自动调用其析构函数,而是等到引用计数为0的时候才会调用__del__,而这个时机是无法控制的。所以在Python中不能将释放紧迫资源的机会依赖于__del__函数,最好是手动释放。

这样可以来解释为什么倒数第二行,为什么swaroo1p就出错,swaroo2p就不出错。问题就出在Python运行垃圾回收的时机不同:

这里我加上gc来显示垃圾回收的时机:
import gc

class Person:
count=0

def __init__(self,name):
self.name=name
self.myname=name # 必须指明self.对象变量名,否则视为函数局部变量
Person.count+=1 # 必须用类名.类变量名
print 'Init! name=%s, count=' % name,Person.count

def __del__(self):
Person.count-=1
print 'Del name=%s, count=' % self.myname,Person.count

gc.set_debug(gc.DEBUG_STATS | gc.DEBUG_LEAK)
swaroop = Person('Swaroop')
swaroo1p = Person('Swaro2op') # swaroo2p不出错,swaroo1p出错。郁闷啊
kalam = Person('Abdul Kalam')

用swaroo1p时,结果:
Init! name=Swaroop, count= 1
Init! name=Swaro2op, count= 2
Init! name=Abdul Kalam, count= 3
Del name=Abdul Kalam, count= 2
Del name=Swaroop, count= 1
gc: collecting generation 2...
gc: objects in each generation: 51 4673 0
gc: done, 0.0010s elapsed.
Exception AttributeError: "'NoneType' object has no attribute 'count'" in <bound method Person.__del__ of <__main__.Person instance at 0x0262D260>> ignored

可以看到在调用Swaroo1p.__del__之前就已经运行了垃圾回收,所以当调用Swaroo1p.__del__的时候,Person已经是None了,对None访问count当然会出错。

现在改成Swaro2op,结果为:
Init! name=Swaroop, count= 1
Init! name=Swaro2op, count= 2
Init! name=Abdul Kalam, count= 3
Del name=Swaro2op, count= 2
Del name=Abdul Kalam, count= 1
Del name=Swaroop, count= 0
gc: collecting generation 2...
gc: objects in each generation: 51 4673 0
gc: done, 0.0000s elapsed.
这次直到调用了swaroo2p.__del__才运行垃圾回收。

不过仍然困惑我的是,到底为什么改了名字就会改变垃圾回收的时机?
我猜与Python的变量存储机制有关,因为可以用Swaro0op, Swaro3op, 再试试,前者出错,后者不出错,道理应该相同。希望有高人来进一步解释这个问题。
angel_su 2010-06-17
  • 打赏
  • 举报
回复
可能是bug吧...
hxDreamer 2010-06-17
  • 打赏
  • 举报
回复
我查了一下网上有类似问题:http://markmail.org/message/dod55fek3bt36axw(这个可能要轻功才能看)

1、错误的真正根源在_PyModule_Clear中
2、Python在销毁运行时环境时,其顺序并非是由dir()显示的那个顺序。因为如果把你的两个变量名由lzh, lds改为a, b,则就没有异常产生了。

我加上了del,看起来可以了:
print dir()
swaroop = Person('Swaroop')
swaroo1p = Person('Swaro2op') # 囧ing
kalam = Person('Abdul Kalam')
print dir()

del swaroop
print dir()
del swaroo1p
print dir()
del kalam
print dir()

说实话《简明 Python 教程》上这样教也不好,虽然说了__del__的作用,但怎么用没指明,初学者郁闷。
晚上回来结贴给分。目前还有问题就是释放类和实例的顺序问题,python不至于在释放所有实例之前先把类本身释放了吧。
hxDreamer 2010-06-17
  • 打赏
  • 举报
回复
7楼解释的回收顺序感觉可能是答案。python对对象堆(如果存在)的管理应该是基于一种无序的字典或映射(Map),这个没有深入研究。
貌似__del__和java的Object.Finalize()有点像,基本上是不用开发人员管的。这几天复习c++脑子乱死了。

下面是官网的解释。(这英语很绕啊。。)
Warning
Due to the precarious circumstances under which __del__() methods are invoked, exceptions that occur during their execution are ignored, and a warning is printed to sys.stderr instead. Also, when __del__() is invoked in response to a module being deleted (e.g., when execution of the program is done), other globals referenced by the __del__() method may already have been deleted or in the process of being torn down (e.g. the import machinery shutting down). For this reason, __del__() methods should do the absolute minimum needed to maintain external invariants. Starting with version 1.5, Python guarantees that globals whose name begins with a single underscore are deleted from their module before other globals are deleted; if no other references to such globals exist, this may help in assuring that imported modules are still available at the time when the __del__() method is called.


另外问一下zxp8216,“把Person.count换成self.__class__.count,将不会出错!”这两个有什么区别,不都是类变量吗(静态变量)?

还有一点疑惑,看起来出错的代码是因为在gc释放了Person类之后又在其他Person类的实例中调用Person类的类变量。我这样描述的对吗?按这么理解,既然Person类还有实例存在,那怎么能“释放”Person类本身?不解。
Del name=Abdul Kalam, count= 2
Del name=Swaroop, count= 1
Exception AttributeError: "'NoneType' object has no attribute 'count'" in <bound method Person.__del__ of <__main__.Person instance at 0x017008A0>> ignored
说明释放Swaroop之后,也“释放(哎,不解,类还有什么释放不释放的啊)”了Person类,然后下面要调用__del__时候找不到count了。


分不够看来要加分了,保证最后都有份。十分感谢大家。
龙根 2010-06-17
  • 打赏
  • 举报
回复
使用域变量时,可以通过self的__class__域来引用
与python的内存回收按照字典先后顺序有关
把Person.count换成self.__class__.count,将不会出错!
原因可能是在idel模式下编译时导致的引用计数计算顺序异常,可以尝试把程序写成文件.py形式来导入,生成.pyc形式的
海楓 2010-06-17
  • 打赏
  • 举报
回复
盡可能避免使用__del__和類空間的共用變量吧。

官方文檔。
http://docs.python.org/reference/datamodel.html#object.__del__
海楓 2010-06-17
  • 打赏
  • 举报
回复
試了下樓主的帖子代碼,swaroo1p和swaroop1的變量名稱的確怪異。
海楓 2010-06-17
  • 打赏
  • 举报
回复

class P:
pc=0
def __init__(self,n):
self.n=n
P.pc+=1
print n,'P.pc:',P.pc


def __del__(self):
P.pc-=1
print self.n,'__del__:',P.pc


p1=P('a')
p2=P('b')
p3=P('c')




winxp python2.6.5 結果正常。

a P.pc: 1
b P.pc: 2
c P.pc: 3
b __del__: 2
c __del__: 1
a __del__: 0

37,722

社区成员

发帖
与我相关
我的任务
社区描述
JavaScript,VBScript,AngleScript,ActionScript,Shell,Perl,Ruby,Lua,Tcl,Scala,MaxScript 等脚本语言交流。
社区管理员
  • 脚本语言(Perl/Python)社区
  • IT.BOB
加入社区
  • 近7日
  • 近30日
  • 至今

试试用AI创作助手写篇文章吧