2017-08-04 23 views
5

Tôi đang cố tạo một lớp chỉ tạo một cá thể nếu các đối số được truyền vào trong quá trình khởi tạo là một kết hợp duy nhất. Nếu sự kết hợp của các đối số đã được truyền vào trước đó, thì trả về cá thể đã được tạo trước đó. Tôi muốn lớp này được thừa kế bởi các lớp khác để chúng kế thừa cùng một hành vi. Đây là nỗ lực của tôi đầu tiên tại một giải pháp,Chỉ khởi tạo các đối tượng duy nhất của một lớp

Các lớp cơ sở/mẹ để được thừa hưởng:

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 

Và đây là cách nó đang được sử dụng trong lớp trẻ:

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 

tôi đã chạy chương trình với thiết lập này nhưng có vẻ như một dict mới đang được tạo ra mỗi khi ProductAdGroup() đang được tạo ra để bộ nhớ bùng nổ ... mặc dù chương trình trả về cá thể đã được khởi tạo trước đó.

Có cách nào để sửa lỗi này không? Cảm ơn !!!

+0

> một dict mới được tạo ra mỗi khi ProductAdGroup() đang được tạo. Làm sao bạn biết ? – aristotll

+0

@aristotll Tôi không chắc là nó có thể. Tôi biết có điều gì đó đang diễn ra với các dicts bởi vì tôi đã kiểm tra xem có bao nhiêu đối tượng được tạo ra cho tất cả các loại tại các điểm khác nhau trong chương trình và các dicts liên tục tăng –

Trả lời

1

Mã của bạn có vẻ đúng - điều duy nhất không chính xác ở trên là phương pháp __init__ của bạn sẽ luôn được gọi khi khởi tạo lớp mới, bất kể phiên bản trước được trả về __new__ hay không. Vì vậy, nếu bạn tạo thêm các đối tượng trong phương thức __init__, có thể là nguyên nhân gây ra rò rỉ bộ nhớ của bạn - tuy nhiên, nếu bạn liên kết các đối tượng mới này với instane (self), chúng sẽ chỉ ghi đè lên một đối tượng đã tạo trước đó trong cùng một nơi - chúng sẽ được giải thoát. . Trong mã được đăng ở đây, điều đó xảy ra với self.name - có thể là __init__ thực của bạn thực hiện nhiều việc hơn và liên kết các đối tượng mới với các địa điểm khác so với trường hợp (ví dụ, đưa chúng vào danh sách). Nếu các phương thức __init__ của bạn cũng giống như thể hiện nguyên nhân cho sự phát triển bộ nhớ của bạn không hiển nhiên trong mã bạn cung cấp.

Là một lời khuyên bổ sung, nhưng không liên quan đến vấn đề bạn liên quan, tôi thêm rằng bạn không cần một metaclass cho điều này cả.

Chỉ cần kiểm tra sự tồn tại của một số cls.instantiated_objects dict trong chính phương thức __new__. Không viết một metaclass không cần thiết sẽ đơn giản hóa codebase của bạn, tránh xung đột metaclass nếu hệ đẳng cấp lớp của bạn phát triển, và thậm chí có thể loại bỏ vấn đề của bạn nếu có nhiều mã trên metaclass hơn bạn đang hiển thị ở đây.

Các cơ sở lớp __new__ phương pháp có thể được viết lại một cái gì đó như thế này:

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 

Và không có nhu cầu nhiều hơn cho một metaclass tùy chỉnh.

Các vấn đề liên quan