Create only unique class objects

I am trying to create a class that only creates an instance if the arguments passed during the instantiation are a unique combination. If a combination of arguments was previously passed, return the instance that was already created earlier. I would like this class to be inherited by other classes, so they inherit the same behavior. This is my first attempt at a solution,

Base / parent class for inheritance:

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 

And here is how it is used in a child class:

 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 

I ran the program with this setting, but it seems that every new file is created every time ProductAdGroup () is created, so that the memory explodes ... although the program returns an instance that was previously instantiated.

Is there any way to fix this? Thanks!!!

+5
source share
1 answer

Your code seems correct - the only thing that is incorrect above is that your __init__ method will always be called when creating an instance of a new class, regardless of the previous instance returned by __new__ or not.

So, if you create additional objects in your __init__ method, this may cause a memory leak, however, if you bind these new objects to instane (self), they shuld just redefine the previously created object in the same place - so that they are freed ., In the code here, this happens with self.name - perhaps your real __init__ does more things and associates new objects with other places than the instance (for example, assigning them to a list). If your __init__ methods are shown exactly the same, the reason for your memory growth is not obvious in the code you supply.

As an additional piece of advice, but not related to the problem you are linking, I add that you don't need a metaclass at all.

Just check for cls.instantiated_objects dict in the __new__ method __new__ . Without writing out an unnecessary metaclass, you simplify your code base, avoid metaclass conflicts if your class hierarchy develops, and can even end your problem if your metaclass has more code than shown here.

The __new__ base class __new__ can be rewritten like this:

 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 

And you no longer need a custom metaclass.

+1
source

Source: https://habr.com/ru/post/1270595/


All Articles