2017-08-04 75 views
5

Sadece örnekleme sırasında iletilen argümanların benzersiz bir kombinasyon olması durumunda bir örnek oluşturan bir sınıf oluşturmaya çalışıyorum. Bağımsız değişkenlerin birleşimi önceden iletilmişse, daha önce oluşturulmuş örneği döndürün. Bu sınıfın diğer sınıflar tarafından miras alınmasını istiyorum, böylece aynı davranışı devralırlar. BuYalnızca bir sınıfın benzersiz nesneleri Instantiate

baz/üst sınıf devralınacak bir çözüm benim ilk denemem olduğu:

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 

Ve bu çocuğun sınıfta kullanılıyor hesaplanır: Ben

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 

programı bu kurulumla çalıştırdı, ancak her zaman ProductAdGroup() oluşturulduğunda her defasında yeni bir dict yaratılıyormuş gibi görünüyor, böylece program daha önce başlatılmış olan örneği döndürse bile bellek patlıyor.

Bunu düzeltmek için yine var mı? Teşekkürler !!!

+0

> ProductAdGroup() her oluşturulduğunda yeni bir dict oluşturuluyor. Nereden biliyorsunuz ? – aristotll

+0

@aristotll Bunun olduğundan emin değilim belki ama. Dicts ile bir şeyler olduğunu biliyorum çünkü programda farklı noktalarda her tür için kaç tane nesne yaratıldığımı kontrol ettim ve diktiler sürekli artıyor –

cevap

1

Kodunuz düzgün görünüyor - yukarıdaki yanlış olan tek şey, __new__ tarafından döndürülen önceki bir örneğe bakılmaksızın, yeni bir sınıf başlatılırken her zaman __init__ yönteminizin çağrılmasıdır.

__init__ yönteminizde fazladan nesneler oluşturursanız, bellek sızıntınızın nedeni bu olabilir, ancak bu yeni nesnelere (kendiliğinden) dokunursanız, bunlar yalnızca önceden oluşturulmuş bir nesneyi geçersiz kılar. aynı yer - ki bunlar serbest bırakılacaktı. . Burada yayınlanan kodda, bu self.name ile gerçekleşir - bu sizin gerçek __init__'un daha fazla şey yapabilmesi ve yeni nesneleri örneğe göre başka yerlere ilişkilendirmesidir (bir listeye götürmek gibi). __init__ yöntemleriniz de gösterildiği gibi, bellek büyütme işleminizin nedeni, sağladığınız kodda belirgin değildir.

Ek bir öneri olarak, ancak ilgili olan sorunla ilgili olarak, bunun için bir metaclassa ihtiyacınız olmadığını ekliyorum.

Yalnızca __new__ yönteminin kendisinde bir cls.instantiated_objects dict varlığını kontrol edin. Gereksiz bir metaclass yazma, kod tabanınızı basitleştirecek, sınıf hiyerarşiniz evrimleşiyorsa metaclass çakışmalarını önleyecek ve metaclass üzerinde burada gösterdiğinden daha fazla kod varsa, probleminizi bile ortadan kaldıracaktır.

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 

Ve özel metaclass artık gerek yoktur:

taban sınıfı __new__ yöntem böyle bir şey tekrar yazılabilir.