New submission from Joël Larose <[email protected]>:
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 <[email protected]>
<https://bugs.python.org/issue43685>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com