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



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

相关内容

  • 没有找到相关文章

最新更新