New submission from Joël Larose <joel.lar...@gmail.com>: Hi,
I'm trying to implement a metaclass for the singleton pattern, with the intent of creating type-appropriate sentinels. After trying several approaches, I've come up with what I thought would be an elegant solution. However, I've run into a bit of a snag. Whenever I "call" the class to get the instance, the machinery behind the scenes always calls __init__. To bypass this, I tried overriding type.__call__ in my metaclass. Contrary to all the documentation I've read, metaclass.__call__ is not being used. The call sequence goes straight to class.__new__ and class.__init__. ===================================================== M = TypeVar("M") class SingletonMeta(type): """Metaclass for single value classes.""" def __call__(cls: Type[M], *args: Any, **kwargs: Any) -> M: ### Never see this line of output print(f"{cls.__name__}.__call__({args=}, {kwargs=}") it: Optional[M] = cast(Optional[M], cls.__dict__.get("__it__")) if it is not None: return it try: it = cls.__new__(*args, **kwargs) it.__init__(*args, **kwargs) except TypeError: it = cls.__new__() it.__init__() # cls.__it__ = it return it def __new__(mcs, name: str, bases: th.Bases, namespace: th.DictStrAny, **kwargs: Any) -> SingletonMeta: print(f"{mcs.__name__}.__new__({name=}, {bases=}, {namespace=}, {kwargs=}") new_cls: SingletonMeta = cast(SingletonMeta, type(name, bases, namespace)) print(f"{new_cls=}") print(f"{new_cls.__call__}") ### Both of these lines ignore the __call__ defined in this metaclass ### They produce TypeError if the class doesn't define __new__ or __init__ accepting arguments # new_cls.__it__ = new_cls(new_cls, **kwargs) # new_cls.__it__ = new_cls.__call__(new_cls, **kwargs) return new_cls Here's the output I get after defining the metaclass and try to use it: >>> class S(metaclass=SingletonMeta): ... pass SingletonMeta.__new__(name='S', bases=(), namespace={'__module__': '__main__', '__qualname__': 'S'}, kwargs={} new_cls=<class '__main__.S'> <method-wrapper '__call__' of type object at 0x000002C1283BF1D0> >>> S() <__main__.S object at 0x000002C128AE5940> >>> S() <__main__.S object at 0x000002C128AE56A0> If SingletonMeta.__call__ was being used, I would see the output from that call, and consecutive calls to S() would yield the same object (with the same address). As you can see, that is not the case. Environment: Python 3.9.0 (tags/v3.9.0:9cf6752, Oct 5 2020, 15:34:40) [MSC v.1927 64 bit (AMD64)] on win32 Is this a bug? Or am I misunderstanding how/when __call__ gets called? ---------- components: Interpreter Core messages: 389947 nosy: joel.larose priority: normal severity: normal status: open title: __call__ not being called on metaclass type: behavior versions: Python 3.9 _______________________________________ Python tracker <rep...@bugs.python.org> <https://bugs.python.org/issue43685> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com