Discussion:
Using __new__
(too old to reply)
Jonathan Gossage
2024-02-17 22:35:21 UTC
Permalink
I am attempting to use the __new__ method in the following code:
class SingletonExample(object):

_instance = None

def __new__(cls, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls, **kwargs)
return cls._instance

def __init__(self, **kwargs) -> None:
our_attributes = ('h', 'x')
if kwargs is not None:
for k, v in kwargs.items():
if k in our_attributes:
setattr(self, k, v)

a = SingletonExample(h=1)

and I get the following result:

(PRV) ***@jfgdev:/PR$ python -m Library.Testing.test2
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/mnt/ProgrammingRenaissance/Library/Testing/test2.py", line 16, in
<module>
a = SingletonExample(h=1)
^^^^^^^^^^^^^^^^^^^^^
File "/mnt/ProgrammingRenaissance/Library/Testing/test2.py", line 6, in
__new__
cls._instance = super().__new__(cls, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: object.__new__() takes exactly one argument (the type to
instantiate)

I am quite puzzled as it looks as if this code will not work if the
super-class is 'object'. Any suggestions on how to proceed?
--
Jonathan Gossage
Stefan Ram
2024-02-17 22:51:29 UTC
Permalink
Above, you specify that the superclass is "object".
Post by Jonathan Gossage
super().__new__(cls, **kwargs)
Now, since you know the superclass to be "object",
why can't you just write "super().__new__( cls )"?
Lawrence D'Oliveiro
2024-02-18 00:19:09 UTC
Permalink
Post by Stefan Ram
Above, you specify that the superclass is "object".
Which is redundant, anyway.
MRAB
2024-02-17 23:08:57 UTC
Permalink
Post by Jonathan Gossage
_instance = None
cls._instance = super().__new__(cls, **kwargs)
return cls._instance
our_attributes = ('h', 'x')
setattr(self, k, v)
a = SingletonExample(h=1)
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/mnt/ProgrammingRenaissance/Library/Testing/test2.py", line 16, in
<module>
a = SingletonExample(h=1)
^^^^^^^^^^^^^^^^^^^^^
File "/mnt/ProgrammingRenaissance/Library/Testing/test2.py", line 6, in
__new__
cls._instance = super().__new__(cls, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: object.__new__() takes exactly one argument (the type to
instantiate)
I am quite puzzled as it looks as if this code will not work if the
super-class is 'object'. Any suggestions on how to proceed?
Don't pass kwargs to object.__new__ because it doesn't expect it.

Incidentally, kwargs will never be None, and there's no point in giving
a return type for __init__ because it can only ever return None.
dn
2024-02-17 23:31:39 UTC
Permalink
Post by Jonathan Gossage
_instance = None
cls._instance = super().__new__(cls, **kwargs)
return cls._instance
our_attributes = ('h', 'x')
setattr(self, k, v)
a = SingletonExample(h=1)
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/mnt/ProgrammingRenaissance/Library/Testing/test2.py", line 16, in
<module>
a = SingletonExample(h=1)
^^^^^^^^^^^^^^^^^^^^^
File "/mnt/ProgrammingRenaissance/Library/Testing/test2.py", line 6, in
__new__
cls._instance = super().__new__(cls, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: object.__new__() takes exactly one argument (the type to
instantiate)
I am quite puzzled as it looks as if this code will not work if the
super-class is 'object'. Any suggestions on how to proceed?
Don't be puzzled. Read the error-message.

Change the super-call to: cls._instance = super().__new__(cls)
and happiness will follow...


That said, mystifications - not sure if this meets the/your definition*
of "singleton", because:

- it can be aliased, eg
a = SingletonExample(h=1)
b = SingletonExample(x=2)

- when it is, the effect is an accumulation of attributes and values
a = SingletonExample(h=1)
b = SingletonExample(h=2)
print( a.__dict__, b.__dict__, )

- it can be re-created with a different value, eg
a = SingletonExample(h=1)
a = SingletonExample(h=2)

- and can be 'regenerated':
a = SingletonExample(h=1)
a = SingletonExample(x=2)

- all failures are silent


* noting "Nowadays, the Singleton pattern has become so popular that
people may call something a singleton even if it solves just one of the
listed problems." (https://refactoring.guru/design-patterns/singleton)


YMMV!
--
Regards,
=dn
dn
2024-02-18 00:05:54 UTC
Permalink
The problem that I am facing is that when the superclass is not
'object', the __init__ method may well need arguments. I do not know how
to determine if the superclass is 'object'. For what it is worth, any
attempt to use this with different arguments should return the initial
singleton and ignore further attempts to create a second instance.
1 "object"
don't understand. Perhaps give us a broader description of the problem?
Remember also ABCs (Abstract Base Classes).

2 arguments
yes, must accommodate arguments in __new__() if some/same are needed in
__init__() However, when using the default "object", the super() does
not need, use, or want, any arguments to be passed.

3 singleton
don't think that is happening!


PS please reply to the list - there may be others who can learn from, or
contribute to, this conversation!
--
Regards,
=dn
Mats Wichmann
2024-02-18 15:52:12 UTC
Permalink
- perhaps someone knows a better/proper way to do this?
Suggested research: custom classes, ABCs, and meta-classes...
Cure the old "what do you want to accomplish" question. If it's to
channel access to a resource to a single place, many folks seem to
advocate just putting that code in a module, and not trying to use a
class for that - Python already treats modules as a form of singleton
(if you squint a bit). It's not Java, after all, everything doesn't
_have_ to be a class.

I'd also second the idea of looking at metaclasses for an
implementation. Most simpler class-based singleton approaches turn out
not to be thread-safe... you can get closer to solving that with a
metaclass with a lock taken in the dunder-call method.

Loading...