如何为我的任务创建适当的元类?

时间:2012-08-09 16:59:06

标签: python weak-references metaclass

我有一个任务创建一个可迭代的类,在 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

3 个答案:

答案 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'))
>>>