仅实例化一个类的唯一对象

Posted

技术标签:

【中文标题】仅实例化一个类的唯一对象【英文标题】:Instantiate only unique objects of a class 【发布时间】:2018-01-12 16:44:45 【问题描述】:

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

要继承的基类/父类:

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

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

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

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

有没有办法解决这个问题? 谢谢!!!

【问题讨论】:

> 每次创建 ProductAdGroup() 时都会创建一个新字典。你怎么知道的? @aristotll 我不确定是不是,但也许吧。我知道 dicts 发生了一些事情,因为我检查了在程序的不同点为所有类型创建了多少对象,并且 dicts 不断增加 【参考方案1】:

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

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

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

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

基类__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

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

【讨论】:

以上是关于仅实例化一个类的唯一对象的主要内容,如果未能解决你的问题,请参考以下文章

设计模式:单例模式

(JAVA)啥是实例化如何实现类的实例化(用类或方法创建一个该类的实例)?

实例化类对象是啥意思?

C#中能否在类的内部实例化一个本类的对象?

C++只能实例化1个对象的类

c# 中类的实例化