0

I am trying to create a Meta-Class for my Class. I have tried to print information about my class in meta-class

Now I have created two objects of my class But Second object gets created without referencing my Meta-Class

Does Meta Class gets called only once per Class??

Any help will be appreciated

Thanks

class Singleton(type):
    def __new__(cls,name,bases,attr):
        print (f"name {name}")
        print (f"bases {bases}")
        print (f"attr {attr}")
        print ("Space Please")

        return super(Singleton,cls).__new__(cls,name,bases,attr)

class Multiply(metaclass = Singleton):
    pass
     
objA = Multiply()
objB = Multiply()

print (objA)
print (objB)
Abhishek Sharma
  • 153
  • 1
  • 2
  • 11
  • Does this answer your question? [What are metaclasses in Python?](https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python) – Tomerikoo Aug 16 '20 at 12:10
  • If you will add a `print("hi")` before the object creation and change to `print(objB.__class__.__class__)` you will see that both objects are part of the meta-class. The body of the `__new__` method is executed once when the `class` statement is reached – Tomerikoo Aug 16 '20 at 12:12

1 Answers1

0

Yes - the metaclass's __new__ and __init__ methods are called only when the class is created. After that, in your example, the class will be bound to theMultiply name. In many aspects, it is just an object like any other in Python. When you do objA = Multiply() you are not creating a new instance of type(Multiply), which is the metaclass - you are creating a new instance of Multiply itself: Multiply.__new__ and Multiply.__init__ are called.

Now, there is this: the mechanism in Python which make __new__ and __init__ be called when creating an instance is the code in the metaclass __call__ method. That is, just as when you create any class with a __call__ method and use an instance of it with the calling syntax obj() will invoke type(obj).__call__(obj), when you do Multiply() what is called (in this case) is Singleton.__call__(Multiply). Since it is not implemented, Singleton's superclass, which is type __call__ method is called instead - and it is in there that Multiply.__new__ and __init__ are called.

That said, there is nothing in the code above that would make your classes behave as "singletons". And more importantly you don't need a metaclass to have a singleton in Python. I don't know who invented this thing, but it keeps circulating around.

First, if you really need a singleton, all you need to do is to write a plain class, no special anything, create your single instance, and document that the instance should be used. Just as people use None - no one keeps getting a reference to Nonetype and keep calling it to get a None reference:

class _Multiply:
   ...

# document that the code should use this instance:
Multiply = _Multiply()

second Alternatively, if your code have a need, whatsoever, for instantiating the class that should be a singleton where it will be used, you can use the class' __new__ method itself to control instantiation, no need for a metaclass:


class Multiply:
     _instance = None
     def __new__(cls):
         if not cls._instance:
             cls._instance = super().__new__(cls)    
             # insert any code that would go in `__init__` here:
             ...
             ...
         return cls._instance

Third just for demonstration purposes, please don't use this, the metaclass mechanism to have singletons can be built in the __call__ method:

class Singleton(type):
    registry = {}
    def __new__(mcls,name,bases,attr):
        print(f"name {name}")
        print(f"bases {bases}")
        print(f"attr {attr}")
        print("Class created")
        print ("Space Please")

        return super(Singleton,mcls).__new__(cls,name,bases,attr)
    
    def __call__(cls, *args, **kw):
        registry = type(cls).registry
        if cls not in registry:
            print(f"{cls.__name__} being instantiated for the first time")
            registry[cls] = super().__call__(*args, **kw)
        else:
            print(f"Attempting to create a new instance of {cls.__name__}. Returning single instance instead")
        return registry[cls]
        

class Multiply(metaclass = Singleton):
    pass
jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • If you consider the question to be answered, you should indicate that by accepting it (check mark close to the vote-score) – jsbueno Aug 17 '20 at 13:27