首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > perl python >

python类和对象判断有关问题请问个大神解决

2013-09-07 
python类和对象判断问题请教个大神解决如果我定义了一个class A,并且用此类实例化了很多对象。那么通过什么

python类和对象判断问题请教个大神解决
如果我定义了一个class A,并且用此类实例化了很多对象。
那么通过什么方法能够查看A现在已经实例化了哪些对象?对象的个数,名字等?
Python 对象 类 实例
[解决办法]
python不直接支持这种功能,你要自己实现。(这儿记录了我尝试的各种办法以及它们的问题,没兴趣看的可以直接跳到最后一段代码,我觉得没有什么问题了。刚看完3个小时的有关meta programming的讲座,忍不住拿来用用,罗嗦了点,大家多多包涵)。

虽然我写了这么多,但我希望你用到它再想想:能否用一个list/set等保存你想保存的实例,而不是让类本身来保存。

In [246]: class InstanceTracking:
     ...:     _instances = []
     ...:     def __init__(self, name):
     ...:         self.name = name
     ...:         InstanceTracking._instances.append(self)
     ...:     @classmethod
     ...:     def listInstances(cls):
     ...:         for instance in cls._instances:
     ...:             print "obj:", instance.name
     ...:     @classmethod
     ...:     def totalInstances(cls):
     ...:         return len(cls._instances)
     ...:     

In [247]: InstanceTracking.totalInstances()
Out[247]: 0

In [248]: A = InstanceTracking('a')

In [249]: InstanceTracking.totalInstances()
Out[249]: 1

In [250]: InstanceTracking.listInstances()
obj: a

In [251]: B = InstanceTracking('b')

In [252]: InstanceTracking.listInstances()
obj: a
obj: b


上面代码的问题是对每一个类,都要自己加上_instances, listInstances, totalInstances等。不能靠继承把这种功能传下去,因为所有的实例都会加到老祖宗那儿,然后一大家子共享:
In [253]: class Fruits(InstanceTracking): 


     ...:     pass

In [254]: apple = Fruits('apple')

In [255]: InstanceTracking.listInstances()
obj: a
obj: b
obj: apple

In [256]: Fruits.listInstances()
obj: a
obj: b
obj: apple



改进一下,在构造实例时动态决定该实例的类,而不是用直接用InstanceTracking。

In [260]: class InstanceTracking2:
     ...:     _instances = []
     ...:     def __init__(self, name):
     ...:         self.name = name
     ...:         self.__class__._instances.append(self)
     ...:     @classmethod
     ...:     def listInstances(cls):
     ...:         for instance in cls._instances:
     ...:             print "obj:", instance.name
     ...:     @classmethod
     ...:     def totalInstances(cls):
     ...:         return len(cls._instances)

In [261]: class Fruit2(InstanceTracking2):
     ...:     _instances = []

In [262]: apple2 = Fruit2('apple')

In [263]: class Car2(InstanceTracking2):
     ...:     _instances = []
     ...:     

In [264]: changan = Car2('changan')

In [265]: Car2.listInstances()
obj: changan

In [266]: Fruit2.listInstances()
obj: apple


很好,汽车和水果不会混到一块了,但是如果你忘了在子类中建立_instance:

In [267]: class Ball2(InstanceTracking2):
     ...:     pass

In [268]: tennis = Ball2('tennis')

In [269]: class City2(InstanceTracking2):
     ...:     pass

In [270]: beijing = City2('Beijing')

In [271]: City2.listInstances()
obj: tennis


obj: Beijing



丢三拉四是不可避免的,最好让机器帮我们记得加上_instances, listInstances等方法:让程序帮我们写程序,meta programming!decorator最容易,用它试试:

In [371]: def addInstanceTracking(cls):
     ...:     cls._instances = []
     ...:     @classmethod
     ...:     def listInstances(cls):
     ...:         for i in cls._instances:
     ...:             print "obj:", i.name
     ...:     @classmethod
     ...:     def totalInstances(cls):
     ...:         return len(cls._instances)
     ...:     old_init = cls.__init__
     ...:     def new_init(self, *arg, **args):
     ...:         self.__class__._instances.append(self)
     ...:         old_init(self, *arg, **args)
     ...:     cls.listInstances = listInstances
     ...:     cls.totalInstances = totalInstances
     ...:     cls.__init__ = new_init
     ...:         
     ...:     return cls

In [372]: @addInstanceTracking
     ...: class Fruit6:
     ...:     def __init__(self, name):
     ...:         self.name = name
     ...:         

In [373]: orange6 = Fruit6('orange')

In [374]: Fruit6.listInstances()
obj: orange

In [375]: class RedFruit6(Fruit6): #decorator对子类没用,所以这儿的效果就像上一个方案中
     ...:     pass                 #子类忘了定义_instances一样

In [376]: strawberry = RedFruit6('strawberry')



In [377]: RedFruit6.listInstances() #子类共享父类的_instances
obj: orange
obj: strawberry

In [378]: Fruit6.listInstances()
obj: orange
obj: strawberry

In [379]: @addInstanceTracking             #再加上decorator试试
     ...: class SummerFruit6(Fruit6):
     ...:     pass

In [379]: 

In [380]: watermalon = SummerFruit6('watermalon')

In [381]: SummerFruit6.listInstances()        #由于SummerFruit6,Fruit6中的__init__
                                              #都有_instances.append的调用,所以加了两遍 
obj: watermalon
obj: watermalon

In [382]: Fruit6.listInstances()
obj: orange
obj: strawberry



我们对类的定义的改造不光要对第一代有效,还要对子子孙孙都有效,metaclass!

In [319]: class InstTracking(type):
     ...:     def __init__(cls, name, bases, dct):
     ...:         type.__init__(cls, name, bases, dct)
     ...:         cls._instances = []
     ...:         @classmethod
     ...:         def listInstances(cls):
     ...:             for i in cls._instances:
     ...:                 print "obj:", i.name
     ...:         @classmethod
     ...:         def totalInstances(cls):
     ...:             return len(cls._instances)
     ...:         if '__init__' in dct:
     ...:             old_init = dct['__init__']
     ...:             def new_init(self, *arg, **args):


     ...:                 self.__class__._instances.append(self)
     ...:                 old_init(self, *arg, **args)
     ...:             cls.__init__ = new_init
     ...:         cls.totalInstances = totalInstances
     ...:         cls.listInstances = listInstances

In [320]: class InstanceTracking4:
     ...:     __metaclass__ = InstTracking
     ...:     pass



现在,每个类只要继承InstanceTracking4就可以了。

In [321]: class Fruit4(InstanceTracking4):
     ...:     def __init__(self, name):
     ...:         self.name = name

In [322]: orange4 = Fruit4('orange')

In [323]: Fruit4.listInstances()
obj: orange

In [324]: class Car4(InstanceTracking4):
     ...:     def __init__(self, name):
     ...:         self.name = name
     ...:         

In [325]: audi4 = Car4('audi')

In [326]: Car4.listInstances()
obj: audi

In [327]: class Truck4(Car4): # 孙子辈的也有这本事
     ...:     pass

In [328]: dongfeng4 = Truck4('dongfeng')

In [330]: Truck4.listInstances()
obj: dongfeng

In [331]: Car4.listInstances()
obj: audi


还有一个问题是_instances总是保留所有实例的引用,所以即使一个实例在其它地方的引用都不存在了,这个实例的内存也不会被回收,造成内存泄露。解决的办法是用weakref。

In [354]: class InstTrackingWeakRef(type):
     ...:     def __init__(cls, name, bases, dct):
     ...:         type.__init__(cls, name, bases, dct)
     ...:         cls._instances = weakref.WeakSet()


     ...:         @classmethod
     ...:         def listInstances(cls):
     ...:             for i in cls._instances:
     ...:                 if i is not None:
     ...:                     print "obj:", i.name # 也许要定义的类没有name,用str(i)更好
     ...:                 else:
     ...:                     cls._instances.remove(i)
     ...:         @classmethod
     ...:         def totalInstances(cls):
     ...:             return len(cls._instances)
     ...:         if '__init__' in dct:
     ...:             old_init = dct['__init__']
     ...:             def new_init(self, *arg, **args):
     ...:                 self.__class__._instances.add(self)
     ...:                 old_init(self, *arg, **args)
     ...:             cls.__init__ = new_init
     ...:         cls.totalInstances = totalInstances
     ...:         cls.listInstances = listInstances
     ...:         

In [355]: class InstanceTrackingWeakRef:
     ...:     __metaclass__ = InstTrackingWeakRef
     ...:     pass



In [356]: class Fruit5(InstanceTrackingWeakRef):
     ...:     def __init__(self, name):
     ...:         self.name = name
     ...:         

In [357]: orange5 = Fruit5('orange')

In [358]: Fruit5.listInstances()
obj: orange

In [359]: del orange5

In [360]: Fruit5.listInstances()

In [361]: Fruit5.totalInstances()
Out[361]: 0

In [362]: class RedFruit5(Fruit5):
     ...:     pass

In [363]: strawberry5 = RedFruit5('strawberry')

In [364]: RedFruit5.listInstances()
obj: strawberry

In [365]: apple = Fruit5('apple')

In [366]: RedFruit5.listInstances()
obj: strawberry

In [367]: Fruit5.listInstances()
obj: apple

In [368]: del strawberry5

In [369]: RedFruit5.listInstances()

In [370]: RedFruit5.totalInstances()
Out[370]: 0

In [371]: 

热点排行