我有一个任务创建一个可迭代的类,在 iter 上返回一个已经从这个类创建的实例列表上的迭代器,例如:
x = SomeClass()
y = SomeClass()
for obj in SomeClass:
print obj
>><__main__.SomeClass object at .......> and etc
我通过元类和globals()完成了它。它看起来很糟糕,但它确实有效。而且我想找到一种更优雅和pythonic的方式;起初我想通过weakref来实现这一点,但我不明白如何从我的元类中从工作类中获取一些变量,当我尝试将refs存储在我的元类中的对象时,它正在创建一个ref给我class,所以这是我的实现:
class ObjectsInMemory(object):
class __metaclass__(type):
def __init__(self, *args, **kwargs):
self.name = args[0]
def __iter__(self):
glob = globals().copy()
cls_inst = []
for _, v in glob.iteritems():
try:
if self.name in v.__repr__():
cls_inst.append(v)
except TypeError:
pass
yield cls_inst
答案 0 :(得分:3)
如果你想跟踪某个类的实例,我建议使用弱引用容器,如weakref.WeakSet
。这种容器类的代码草图:
import weakref
class InstanceTracker(object):
def __init__(self):
self.instances = {}
def new(self, cls, *args, **kwargs):
instance = cls(*args, **kwargs)
self.instances.setdefault(cls, weakref.WeakSet()).add(instance)
return instance
def iter_instances(cls):
return iter(self.instances[cls])
您现在可以为SomeClass
实例tracker.new(SomeClass)
创建InstanceTracker
tracker
的新实例。
这样,您不必依赖全局状态,您的函数没有奇怪的副作用,并且您不需要修改类来跟踪其实例。
答案 1 :(得分:2)
老实说,我认为你对所寻求的功能有错误的方法。
首先,全局变量很糟糕 - 如果你可以的话,请避开它们。其次,你真的不需要创建一个元类来创建一个全局迭代。而是尝试以下方法:
class SomeClass():
ALL_INSTANCES = set()
def __init__(self):
self.ALL_INSTANCES.add(self)
for obj in SomeClass.ALL_INSTANCES:
print obj
这为您提供了相同的可迭代功能,而没有可怕的元类混乱。它还隐藏了SomeClass范围内的全局定义,因此您对全局变量的担忧较少(尽管不是全部,因为它仍保持全局状态)。
请注意,您无法轻松删除ALL_INSTANCES中的对象,因为只要Class类型存在,您就不会自动调用__del__
,因为您本身就有对该对象的引用。定义del
的手动__del__
调用将从全局集中删除对象(一旦最后一个引用消失 - 除非有循环引用),但这依赖于用户记住这样做。用这种结构说,你应该假设所有物体都无限期地存在。
或者您可以在WeakSet中使用弱引用,并确保__del__
实际上从工作集中删除。但由于循环引用(至少),它仍然无法保证被调用,所以'死'对象可能仍然存在于全局实例中。
答案 2 :(得分:1)
这是一个有趣的元类要求,但在Python 3中编写相当简单(花了几分钟)。
<强>代码:强>
import weakref
class InstanceIter(type):
def __new__(cls, name, bases, class_dict):
new_class = super().__new__(cls, name, bases, class_dict)
new_class.__instances = weakref.WeakSet()
return new_class
def __call__(self, *args, **kwargs):
instance = super().__call__(*args, **kwargs)
self.__instances.add(instance)
return instance
def __iter__(self):
return iter(self.__instances)
class SomeClass(metaclass=InstanceIter):
def __init__(self, name):
self.name = name
def __repr__(self):
return '{!s}({!r})'.format(self.__class__.__name__, self.name)
测试以确保代码工作变得简单。弱引用有助于记忆。
<强>测试强>
>>> a, b, c, d, e = (SomeClass(name) for name in 'vwxyz')
>>> tuple(SomeClass)
(SomeClass('v'), SomeClass('y'), SomeClass('x'), SomeClass('z'), SomeClass('w'))
>>>