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

Python 如何为任务创建适当的元类?,python,weak-references,metaclass,Python,Weak References,Metaclass,我的任务是创建一个iterable类,该类在iter上返回一个迭代器,该迭代器位于已经从该类创建的实例列表上,例如: x = SomeClass() y = SomeClass() for obj in SomeClass: print obj >><__main__.SomeClass object at .......> and etc 老实说,我认为您对所寻求的功能采用了错误的方法 首先,全球化是不好的——尽可能避免全球化。第二,您真的不需要创建一个元类来

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

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

老实说,我认为您对所寻求的功能采用了错误的方法

首先,全球化是不好的——尽可能避免全球化。第二,您真的不需要创建一个元类来创建一个全局iterable。请尝试以下方法:

class SomeClass():
    ALL_INSTANCES = set()
    def __init__(self):
        self.ALL_INSTANCES.add(self)

for obj in SomeClass.ALL_INSTANCES:
    print obj
这为您提供了相同的iterable功能,而没有可怕的元类混乱。此外,它还将全局定义隐藏在SomeClass范围内,因此您不必担心全局变量(尽管不是全部,因为它仍然保持全局状态)

请注意,您无法轻松删除ALL_实例中的对象,因为只要类类型存在,就不会自动调用
\uuu del_u
,因为您固有地拥有对该对象的引用。定义了
\uuu del\uuu
的手动
del
调用将从全局集中删除对象(一旦最后一个引用消失——除非存在循环引用),但这取决于用户是否记得这样做。也就是说,对于这种结构,你应该假设所有的对象都无限期地存在


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

老实说,我认为您对所寻求的功能的方法是错误的

首先,全球化是不好的——尽可能避免全球化。第二,您真的不需要创建一个元类来创建一个全局iterable。请尝试以下方法:

class SomeClass():
    ALL_INSTANCES = set()
    def __init__(self):
        self.ALL_INSTANCES.add(self)

for obj in SomeClass.ALL_INSTANCES:
    print obj
这为您提供了相同的iterable功能,而没有可怕的元类混乱。此外,它还将全局定义隐藏在SomeClass范围内,因此您不必担心全局变量(尽管不是全部,因为它仍然保持全局状态)

请注意,您无法轻松删除ALL_实例中的对象,因为只要类类型存在,就不会自动调用
\uuu del_u
,因为您固有地拥有对该对象的引用。定义了
\uuu del\uuu
的手动
del
调用将从全局集中删除对象(一旦最后一个引用消失——除非存在循环引用),但这取决于用户是否记得这样做。也就是说,对于这种结构,你应该假设所有的对象都无限期地存在


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

如果您想跟踪某个类的实例,我建议为此使用弱引用容器,如
weakref.WeakSet
。此类容器类的代码草图:

导入weakref
类InstanceTracker(对象):
定义初始化(自):
self.instances={}
def新(自身、cls、*ARG、**kwargs):
实例=cls(*args,**kwargs)
self.instances.setdefault(cls,weakref.WeakSet()).add(实例)
返回实例
定义iter_实例(cls):
返回iter(self.instances[cls])
现在,您可以使用
tracker.new(SomeClass)
InstanceTracker
实例
tracker
创建
SomeClass
的新实例


这样,您就不必依赖全局状态,您的函数也不会产生奇怪的副作用,您也不需要修改类来跟踪其实例。

如果您想跟踪某个类的实例,我建议为此使用弱引用容器,如
weakref.WeakSet
。此类容器类的代码草图:

导入weakref
类InstanceTracker(对象):
定义初始化(自):
self.instances={}
def新(自身、cls、*ARG、**kwargs):
实例=cls(*args,**kwargs)
self.instances.setdefault(cls,weakref.WeakSet()).add(实例)
返回实例
定义iter_实例(cls):
返回iter(self.instances[cls])
现在,您可以使用
tracker.new(SomeClass)
InstanceTracker
实例
tracker
创建
SomeClass
的新实例


这样,您就不必依赖全局状态,您的函数没有奇怪的副作用,您也不需要修改类来跟踪其实例。

这是一个有趣的元类要求,但用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'))
>>> 
确保代码正常工作的测试结果也很简单。弱引用有助于记忆


测试:

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

这是一个有趣的元类需求,但用Python3编写起来相当简单(花了几分钟)


代码:

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'))
>>> 
确保代码正常工作的测试结果也很简单。弱引用有助于记忆


测试:

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

+1对于InstanceTracker对象的想法——尽管需要更多的管理来保持它,但它比元类或全局类干净得多。编写元类只需要额外的两行代码,一旦你了解了元类是如何工作的,这就很简单了。@NoctisSkytower:我知道如何使用元类。无论如何,我不喜欢这个解决方案,因为它隐藏了全局状态,而且类迭代行为违反直觉。实现这一点可能是一个有趣的练习,但在实践中,我更喜欢更简单、更明确的解决方案。@SvenMarnach:今年夏天之前我不理解元类,但我强迫自己对元类了解更多,并对它们提供的强大功能感到惊讶。+1表示InstanceTracker对象想法--