仅实例化类的唯一对象

Ale*_*ias 6 oop memory-leaks metaclass instantiation python-3.5

我正在尝试创建一个只在实例化过程中传入的参数是唯一组合时才创建实例的类.如果先前已传入参数组合,则返回先前已创建的实例.我希望这个类可以被其他类继承,所以它们继承了相同的行为.这是我第一次尝试解决方案,

要继承的基类/父类:

class RegistryType(type):
    def __init__(cls, name, bases, namespace, *args):
        cls.instantiated_objects = {}


class AdwordsObject(object, metaclass=RegistryType):
    api = AdWordsAPI()

    def __new__(cls, *args):
        object_name = '-'.join(args)
        if object_name in cls.instantiated_objects:
            return cls.instantiated_objects[object_name]
        else:
            obj = super(AdwordsObject, cls).__new__(cls)
            cls.instantiated_objects[object_name] = obj
            # cls.newt_connection.commit()
            return obj
Run Code Online (Sandbox Code Playgroud)

这就是它在子类中的使用方式:

class ProductAdGroup(AdwordsObject):
    # init method only called if object being instantiated hasn't already been instantiated
    def __init__(self, product_name, keyword_group):
        self.name = '-'.join([product_name, keyword_group])

    @classmethod
    def from_string(cls, name: str):
        arguments = name.split('-')
        assert len(arguments) == 2, 'Incorrect ad group name convention. ' \
                                    'Use: Product-KeywordGroup'
        ad_group = cls(*arguments)
        return ad_group
Run Code Online (Sandbox Code Playgroud)

我已经使用此设置运行程序,但似乎每次创建ProductAdGroup()时都会创建一个新的dict,因此内存正在爆炸...即使程序返回已经实例化过的实例.

有没有什么办法解决这一问题?谢谢!!!

jsb*_*eno 2

您的代码似乎是正确的 - 上面唯一不正确的是,__init__在实例化新类时始终会调用您的方法,无论是否返回先前的实例__new__

因此,如果您在__init__方法中创建额外的对象,这可能是内存泄漏的原因 - 但是,如果您将这些新对象绑定到实例(自身),它们应该只是在同一位置覆盖以前创建的对象 - 这他们会被释放吗?。在此处发布的代码中,这种情况发生self.name- 可能是您的真实对象 __init__做了更多的事情,并将新对象关联到实例之外的其他位置(例如,将它们附加到列表中)。如果您的__init__方法如所示,那么您提供的代码中内存增长的原因并不明显。

作为额外的建议,但与您所涉及的问题无关,我补充说您根本不需要元类。

cls.instantiated_objects只需检查方法本身是否存在字典即可__new__。不编写不需要的元类将简化您的代码库,在类层次结构发展时避免元类冲突,如果您的元类上的代码比您在此处显示的代码多,甚至可能消除您的问题。

基类__new__方法可以重写如下:

class AdwordsObject(object):
    def __new__(cls, *args):
        if not cls.__dict__.get("instantiated_objects"):
            cls.instantiated_objects = {}
        name = '-'.join(args)
        if name in cls.instantiated_objects:
            return cls.instantiated_objects[name]
        instance = super().__new__(cls)
        cls.instantiated_objects[name] = instance
        return instance
Run Code Online (Sandbox Code Playgroud)

并且不再需要自定义元类。