1

我有创建一个可迭代类的任务,该类在iter上返回已经从此类创建的实例列表上的迭代器,例如:

x = SomeClass()
y = SomeClass()
for obj in SomeClass:
    print obj
>><__main__.SomeClass object at .......> and etc

我通过元类和 globals() 实现了它。它看起来很糟糕,但它确实有效。我想找到一种更优雅和pythonic的方式;起初我想通过一个弱引用来做到这一点,但我不明白如何从我的元类中从工作类中获取一些变量,当我尝试将引用存储在我的元类中的对象上时,它正在为我的对象创建一个引用类,所以这是我的实现:

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
4

3 回答 3

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])

您现在可以为 instance 创建新的SomeClasswith实例。tracker.new(SomeClass)InstanceTrackertracker

这样,您不必依赖全局状态,您的函数不会有奇怪的副作用,并且您不需要修改一个类来跟踪它的实例。

于 2012-08-09T17:15:37.987 回答
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__del__中的对象,因为只要 Class 类型存在,您就固有地引用该对象,因此不会自动调用。使用定义的手动del调用__del__将从全局集中删除对象(一旦最后一个引用消失 - 除非存在循环引用),但这依赖于用户记住这样做。话虽如此,您应该假设所有对象都无限期地存在。

或者您可以在 WeakSet 中使用弱引用,并确保__del__实际从工作集中删除。但是由于循环引用(至少),它仍然不能保证被调用,所以“死”对象可能仍然存在于全局的活动实例中。

于 2012-08-09T17:12:57.810 回答
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'))
>>> 
于 2012-08-09T19:18:25.900 回答