2017-08-04 32 views
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 

這是它如何在子類中使用:

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()時都會創建一個新的字典,以便內存爆炸......即使程序返回之前已經實例化的實例。

有沒有辦法解決這個問題? 謝謝!

+0

>每次創建ProductAdGroup()時都會創建一個新的字典。你怎麼知道的 ? – aristotll

+0

@aristotll我不確定這是可能的。我知道有些事情正在進行,因爲我檢查了在程序的不同點上爲所有類型創建了多少個對象,並且字典不斷增加 –

回答

1

您的代碼似乎是正確的 - 上面唯一不正確的是,在實例化新類時總是會調用您的__init__方法,而不管之前的實例是否由__new__返回。因此,如果你在你的__init__方法中創建了額外的對象,那可能是你的內存泄漏的原因 - 然而,如果你將這些新對象綁定到instane(self),他們就會覆蓋之前創建的對象同一個地方 - 他們將被釋放。 。在這裏發佈的代碼中,這發生在self.name - 這可能是你的真實__init__做更多的事情,並將新的對象與實例關聯的其他地方(比如,將它們拖到列表中)。如果您的__init__方法正如所示,您的內存增長的原因在您提供的代碼中不明顯。

作爲一個額外的建議,但與您所涉及的問題無關,我補充說您完全不需要元類。

只需檢查__new__方法本身中是否存在cls.instantiated_objects字典。不寫一個不需要的元類將簡化你的代碼庫,如果你的類層次結構發展,避免元類衝突,如果你的元類有更多的代碼比你在這裏展示的更多,你甚至可以消除你的問題。

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

而且還有一個自定義元類不再需要。