我的任务是创建一个可迭代的类,该类在iter上返回已从该类创建的实例列表中的迭代器,例如:
x = SomeClass()
y = SomeClass()
for obj in SomeClass:
print obj
>><__main__.SomeClass object at .......> and etc
我通过元类和 globals() 做到了。它看起来很糟糕,但它有效。我想找到一种更优雅和蟒蛇的方式;起初我想通过弱引用来做到这一点,但我不明白如何从我的元类中从工人阶级中获取一些变量,当我尝试将 refs 存储在我的元类中的对象上时,它正在为我的类创建一个 ref,所以这是我的实现:
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
如果你想跟踪某个类的实例,我建议为此使用弱引用容器,比如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])
现在,您可以为InstanceTracker
实例tracker
创建具有tracker.new(SomeClass)
的SomeClass
的新实例。
不必依赖全局状态,您的函数没有奇怪的副作用,并且您不需要修改类来跟踪其实例。
老实说,我认为您对所寻求的功能有错误的方法。
首先,全局变量是不好的——如果可以的话,请避免使用它们。其次,您真的不需要创建一个元类来创建全局可迭代对象。相反,请尝试以下操作:
class SomeClass():
ALL_INSTANCES = set()
def __init__(self):
self.ALL_INSTANCES.add(self)
for obj in SomeClass.ALL_INSTANCES:
print obj
这为您提供了相同的可迭代功能,而没有元类的可怕混乱。此外,它将全局定义隐藏在 SomeClass 范围内,因此您不必担心全局变量(尽管不是全部,因为它仍然保持全局状态)。
请注意,您无法轻松删除ALL_INSTANCES中的对象,因为只要类类型存在__del__
您就不会自动调用对象。定义__del__
的手动del
调用将从全局集中删除对象(一旦最后一个引用消失 - 除非有循环引用),但这依赖于用户记住这样做。话虽如此,对于这种结构,您应该假设所有对象都无限期地存在。
或者,您可以在弱集中使用弱引用,并确保__del__
实际从工作集中删除。但是由于循环引用(至少)的原因,它仍然不能保证被调用,因此"死"对象可能仍存在于全局的实时实例中。
这是一个有趣的元类要求,但在 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'))
>>>