Mailing List Archive

type annotation vs working code
A type annotation isn't supposed to change what code does,
or so I thought:

#------------------------------------------------------------
class Borg:
_instances:dict = {}

def __new__(cls, *args, **kargs):
# look up subclass instance cache
if Borg._instances.get(cls) is None:
Borg._instances[cls] = object.__new__(cls)
return Borg._instances[cls]


class WorkingSingleton(Borg):

def __init__(self):
print(self.__class__.__name__, ':')
try:
self.already_initialized
print('already initialized')
return

except AttributeError:
print('initializing')

self.already_initialized = True
self.special_value = 42


class FailingSingleton(Borg):

def __init__(self):
print(self.__class__.__name__, ':')
try:
self.already_initialized:bool
print('already initialized')
return

except AttributeError:
print('initializing')

self.already_initialized = True
self.special_value = 42

s = WorkingSingleton()
print(s.special_value)

s = FailingSingleton()
print(s.special_value)

#------------------------------------------------------------

Notice how Working* and Failing differ in the type annotation
of self.already_initialized only.

Output:

WorkingSingleton :
initializing
42

FailingSingleton :
already initialized <====================== Huh ?
Traceback (most recent call last):
File "/home/ncq/Projekte/gm/git/gnumed/gnumed/client/testing/test-singleton.py", line 48, in <module>
print(s.special_value)
^^^^^^^^^^^^^^^
AttributeError: 'FailingSingleton' object has no attribute 'special_value'


Where's the error in my thinking (or code) ?

Thanks,
Karsten
--
GPG 40BE 5B0E C98E 1713 AFA6 5BC0 3BEA AC80 7D4F C89B
--
https://mail.python.org/mailman/listinfo/python-list
Re: type annotation vs working code [ In reply to ]
On 9/30/23 13:00, Karsten Hilbert via Python-list wrote:
> A type annotation isn't supposed to change what code does,
> or so I thought:
>
> #------------------------------------------------------------
> class Borg:
> _instances:dict = {}
>
> def __new__(cls, *args, **kargs):
> # look up subclass instance cache
> if Borg._instances.get(cls) is None:
> Borg._instances[cls] = object.__new__(cls)
> return Borg._instances[cls]
>
>
> class WorkingSingleton(Borg):
>
> def __init__(self):
> print(self.__class__.__name__, ':')
> try:
> self.already_initialized
> print('already initialized')
> return
>
> except AttributeError:
> print('initializing')
>
> self.already_initialized = True
> self.special_value = 42
>
>
> class FailingSingleton(Borg):
>
> def __init__(self):
> print(self.__class__.__name__, ':')
> try:
> self.already_initialized:bool
> print('already initialized')
> return
>
> except AttributeError:
> print('initializing')
>
> self.already_initialized = True
> self.special_value = 42
>
> s = WorkingSingleton()
> print(s.special_value)
>
> s = FailingSingleton()
> print(s.special_value)
>
> #------------------------------------------------------------
>
> Notice how Working* and Failing differ in the type annotation
> of self.already_initialized only.

What happens here is in the second case, the line is just recorded as a
variable annotation, and is not evaluated as a reference, as you're
expecting to happen, so it just goes right to the print call without
raising the exception. You could change your initializer like this:

def __init__(self):
print(self.__class__.__name__, ':')
self.already_initialized: bool
try:
self.already_initialized
print('already initialized')
return

The syntax description is here:

https://peps.python.org/pep-0526/#global-and-local-variable-annotations


--
https://mail.python.org/mailman/listinfo/python-list
Re: type annotation vs working code [ In reply to ]
On 01/10/2023 08.00, Karsten Hilbert via Python-list wrote:
> A type annotation isn't supposed to change what code does,
> or so I thought:
>
> #------------------------------------------------------------
> class Borg:
> _instances:dict = {}
>
> def __new__(cls, *args, **kargs):
> # look up subclass instance cache
> if Borg._instances.get(cls) is None:
> Borg._instances[cls] = object.__new__(cls)
> return Borg._instances[cls]
>
>
> class WorkingSingleton(Borg):
>
> def __init__(self):
> print(self.__class__.__name__, ':')
> try:
> self.already_initialized
> print('already initialized')
> return
>
> except AttributeError:
> print('initializing')
>
> self.already_initialized = True
> self.special_value = 42
>
>
> class FailingSingleton(Borg):
>
> def __init__(self):
> print(self.__class__.__name__, ':')
> try:
> self.already_initialized:bool
> print('already initialized')
> return
>
> except AttributeError:
> print('initializing')
>
> self.already_initialized = True
> self.special_value = 42
>
> s = WorkingSingleton()
> print(s.special_value)
>
> s = FailingSingleton()
> print(s.special_value)
>
> #------------------------------------------------------------
>
> Notice how Working* and Failing differ in the type annotation
> of self.already_initialized only.
>
> Output:
>
> WorkingSingleton :
> initializing
> 42
>
> FailingSingleton :
> already initialized <====================== Huh ?
> Traceback (most recent call last):
> File "/home/ncq/Projekte/gm/git/gnumed/gnumed/client/testing/test-singleton.py", line 48, in <module>
> print(s.special_value)
> ^^^^^^^^^^^^^^^
> AttributeError: 'FailingSingleton' object has no attribute 'special_value'
>
>
> Where's the error in my thinking (or code) ?

What is your thinking?
Specifically, what is the purpose of testing self.already_initialized?

Isn't it generally regarded as 'best practice' to declare (and define a
value for) all attributes in __init__()? (or equivalent) In which case,
it will (presumably) be defined as False; and the try-except reworded to
an if-else.

Alternately, how about using hasattr()? eg

if hasattr( self.already_initialized, 'attribute_name' ):
# attribute is defined, etc


As the code current stands, the:

try:
self.already_initialized

line is flagged by the assorted linters, etc, in my PyCharm as:

Statement seems to have no effect.
Unresolved attribute reference 'already_initialized' for class
'WorkingSingleton'.

but:

self.already_initialized:bool

passes without comment (see @Mats' response).


Question: is it a legal expression (without the typing)?

--
Regards,
=dn
--
https://mail.python.org/mailman/listinfo/python-list
Re: type annotation vs working code [ In reply to ]
Am Sun, Oct 01, 2023 at 09:04:05AM +1300 schrieb dn via Python-list:

> >class WorkingSingleton(Borg):
> >
> > def __init__(self):
> > print(self.__class__.__name__, ':')
> > try:
> > self.already_initialized
> > print('already initialized')
> > return
> >
> > except AttributeError:
> > print('initializing')
> >
> > self.already_initialized = True
> > self.special_value = 42

> >Where's the error in my thinking (or code) ?
>
> What is your thinking?
> Specifically, what is the purpose of testing self.already_initialized?

The purpose is to check whether the singleton class has been
... initialized :-)

The line

self.already_initialized = True

is misleading as to the fact that it doesn't matter at all
what self.already_initialized is set to, as long as is
exists for the next time around.

> Isn't it generally regarded as 'best practice' to declare (and define a value for) all
> attributes in __init__()? (or equivalent) In which case, it will (presumably) be defined
> as False; and the try-except reworded to an if-else.

I fail to see how that can differentiate between first-call
and subsequent call.

> Alternately, how about using hasattr()? eg
>
> if hasattr( self.already_initialized, 'attribute_name' ):

That does work. I am using that idiom in other children of
Borg. But that's besides the point. I was wondering why it
does not work the same way with and without the type
annotation.

> try:
> self.already_initialized
>
> line is flagged by the assorted linters, etc, in my PyCharm as:
>
> Statement seems to have no effect.

Well, the linter simply cannot see the purpose, which is
test-of-existence.

> Question: is it a legal expression (without the typing)?

It borders on the illegal, I suppose, as the self-
introspection capabilities of the language are being
leveraged to achieve a legal purpose.

Which seems akin constructs for generating compatibility
between versions.

It seems the answer is being pointed to in Matts response.

It just mightily surprised me.

Karsten
--
GPG 40BE 5B0E C98E 1713 AFA6 5BC0 3BEA AC80 7D4F C89B
--
https://mail.python.org/mailman/listinfo/python-list
Re: type annotation vs working code [ In reply to ]
On 01/10/2023 11.25, Karsten Hilbert via Python-list wrote:
> Am Sun, Oct 01, 2023 at 09:04:05AM +1300 schrieb dn via Python-list:
>
>>> class WorkingSingleton(Borg):
>>>
>>> def __init__(self):
>>> print(self.__class__.__name__, ':')
>>> try:
>>> self.already_initialized
>>> print('already initialized')
>>> return
>>>
>>> except AttributeError:
>>> print('initializing')
>>>
>>> self.already_initialized = True
>>> self.special_value = 42
>
>>> Where's the error in my thinking (or code) ?
>>
>> What is your thinking?
>> Specifically, what is the purpose of testing self.already_initialized?

Apologies, my tending to use the "Socratic Method" with trainees (and
avoiding any concept of personal-fault with others), means it can be
difficult to tell if (personal*) introspection is being invited, or if I
don't know the answer (and want to).

* personal cf Python code introspection (hah!)


> The purpose is to check whether the singleton class has been
> ... initialized :-)
>
> The line
>
> self.already_initialized = True
>
> is misleading as to the fact that it doesn't matter at all
> what self.already_initialized is set to, as long as is
> exists for the next time around.
>
>> Isn't it generally regarded as 'best practice' to declare (and define a value for) all
>> attributes in __init__()? (or equivalent) In which case, it will (presumably) be defined
>> as False; and the try-except reworded to an if-else.
>
> I fail to see how that can differentiate between first-call
> and subsequent call.

+1


>> Alternately, how about using hasattr()? eg
>>
>> if hasattr( self.already_initialized, 'attribute_name' ):
>
> That does work. I am using that idiom in other children of
> Borg. But that's besides the point. I was wondering why it
> does not work the same way with and without the type
> annotation.

Annotations are for describing the attribute. In Python we don't have
different instructions for declaring an object and defining it, eg

INTEGER COUNTER
COUNTER = 0

Thus, Python conflates both into the latter, ie

counter = 0
or
counter:int = 0

(both have the same effect in the Python Interpreter, the latter aims to
improve documentation/reading/code-checking)

Typing defines (or rather labels) the object's type. Accordingly, occurs
when the object is on the LHS (Left-hand Side) of an expression (which
includes function-argument lists).

In this 'test for existence': in the case of WorkingSingleton(), the
code-line is effectively only a RHS - see 'illegal' (below).

However, the annotation caused the code-line to be re-interpreted as
some sort of LHS in FailingSingleton().
- as explained (@Mats) is understood as a 'typing expression' rather
than 'Python code'.

Apologies: fear this a rather clumsy analysis - will welcome improvement...


>> try:
>> self.already_initialized
>>
>> line is flagged by the assorted linters, etc, in my PyCharm as:
>>
>> Statement seems to have no effect.
>
> Well, the linter simply cannot see the purpose, which is
> test-of-existence.
>

> Question: is it a legal expression (without the typing)?
>
> It borders on the illegal, I suppose, as the self-
> introspection capabilities of the language are being
> leveraged to achieve a legal purpose.


...and so we're addressing the important question: the try-test is for
existence, cf for some value.

This can also be achieved by using the attribute in a legal expression, eg:

self.already_initialized == True


When introspecting code, if type-checkers cannot determine the purpose,
is there likely to be a 'surprise factor' when a human reads it?
(that's Socratic! I already hold an opinion: right or wrong)


Might this remove the confusion (ref: @Mats):

self.already_initialized:bool == True

(not Socratic, don't know, haven't tested)



> Which seems akin constructs for generating compatibility
> between versions.

versions of ?


> It seems the answer is being pointed to in Matts response.
>
> It just mightily surprised me.

Me too!


I am slightly confused (OK, OK!) and probably because I don't have a
good handle on "Borg" beyond knowing it is a Star Wars?Trek reference
(apologies to the reader sucking-in his/her breath at such an utterance!).

What is the intent: a class where each instance is aware of every other
instance - yet the word "Singleton" implies there's only one (cf a dict
full of ...)?


Second move (also, slightly) off-topic:
I'm broadly in-favor of typing; additionally noting that trainees find
it helpful whilst developing their code-reading skills. However, am not
particularly zealous in my own code, particularly if the type-checker
starts 'getting picky' with some construct and taking-up
time/brain-power. (which is vitally-required for writing/testing Python
code!)

So, (original code-sample, second line), seeing we ended-up talking
about a type-definition cf attribute-definition, do you (gentle reader,
as well as @OP, if inclined) feel an excess of 'boiler-plate' in the
likes of:

_instances:dict = {}

we write ":dict", yet doesn't the RHS's "{}" communicate exactly the
same (to us, and to dev.tools)?
NB for reasons described, I'll habitually type the typing!
But...


Thanks for the thought-provoking question!

--
Regards,
=dn
--
https://mail.python.org/mailman/listinfo/python-list
Re: type annotation vs working code [ In reply to ]
Sorry for having conflated the core of the matter with all
the Borg shenanigans, that's where I found the problem in my
real code, so there :-)

Consider this:

#----------------------------------------------------
class Surprise:
def __init__(self, with_type_annotation=False):
if with_type_annotation:
try:
self.does_not_exist:bool
print('does_not_exist does exist')
except AttributeError:
print('does_not_exist does not exist')
return

try:
self.does_not_exist
print('does_not_exist does exist')
except AttributeError:
print('does_not_exist does not exist')

Surprise(with_type_annotation = False)
Surprise(with_type_annotation = True)
#----------------------------------------------------

Is this how it is supposed to be ?


> ...and so we're addressing the important question: the try-test is for existence, cf for
> some value.
>
> This can also be achieved by using the attribute in a legal expression, eg:
...
> Might this remove the confusion (ref: @Mats):
>
> self.already_initialized:bool == True

Not for me as that would _create_ already_initialized on the
instance. It would not allow me to test for it.

> >Which seems akin constructs for generating compatibility
> >between versions.
>
> versions of ?

Of the language. Sometimes one tests for existence of a given
class in a module and defines said class oneself if it does
not exist. But that's leading astray.

> What is the intent: a class where each instance is aware of every other instance - yet
> the word "Singleton" implies there's only one (cf a dict full of ...)?

The latter.

Regards,
Karsten
--
GPG 40BE 5B0E C98E 1713 AFA6 5BC0 3BEA AC80 7D4F C89B
--
https://mail.python.org/mailman/listinfo/python-list
Re: type annotation vs working code [ In reply to ]
On Sun, 1 Oct 2023 at 22:58, Karsten Hilbert via Python-list
<python-list@python.org> wrote:
>
> Sorry for having conflated the core of the matter with all
> the Borg shenanigans, that's where I found the problem in my
> real code, so there :-)
>
> Consider this:
>
> #----------------------------------------------------
> class Surprise:
> def __init__(self, with_type_annotation=False):
> if with_type_annotation:
> try:
> self.does_not_exist:bool
> print('does_not_exist does exist')
> except AttributeError:
> print('does_not_exist does not exist')
> return
>
> try:
> self.does_not_exist
> print('does_not_exist does exist')
> except AttributeError:
> print('does_not_exist does not exist')
>
> Surprise(with_type_annotation = False)
> Surprise(with_type_annotation = True)
> #----------------------------------------------------
>
> Is this how it is supposed to be ?

The class isn't even significant here. What you're seeing is simply
that an annotation does not evaluate the expression.

https://peps.python.org/pep-0526/

It's basically a coincidence that your two versions appear nearly
identical. They are quite different semantically. Note that annotating
the expression "self.does_not_exist" is not particularly meaningful to
Python, and I've no idea what different type checkers will do with it;
you normally only annotate variables that you own - so, in a function,
that's function-local variables. Instead, class and instance
attributes should be annotated at the class level, which would remove
this apparent similarity.

This is a very good reason NOT to arbitrarily add type hints to code.
Type hints do not inherently improve code, and making changes just for
the sake of adding them risks making semantic changes that you didn't
intend. Python uses a system of gradual typing for very good reason;
you should be able to add hints only to the places where they're
actually useful, leaving the rest of the code untouched.

ChrisA
--
https://mail.python.org/mailman/listinfo/python-list
Re: type annotation vs working code [ In reply to ]
My view of the issue is that the "trick" of "evaluating" a name to see
if the object has been initialized is just a tad on the "tricky" side,
and the annotation/value is really incorrect.

The name at the point you are annotating it, isn't really a "bool"
because a bool will always have either the value "True" or "False",
while for this variable, you are really testing if it exists or not.

Perhaps a better method would be rather than just using the name and
catching the exception, use a real already_initialized flag (set to True
when you initialize), and look it up with getattr() with a default value
of False.

--
Richard Damon

--
https://mail.python.org/mailman/listinfo/python-list
Re: type annotation vs working code [ In reply to ]
> On 1 Oct 2023, at 19:36, Richard Damon via Python-list <python-list@python.org> wrote:
>
> Perhaps a better method would be rather than just using the name and catching the exception, use a real already_initialized flag (set to True when you initialize), and look it up with getattr() with a default value of False.
I would use a class variable not an instance variable.

class OnlyOne:
sole_instance = None
def __init__(self):
assert OnlyOne.sole_instance is None
OnlyOne.sole_instance = self

Barry


--
https://mail.python.org/mailman/listinfo/python-list
Re: type annotation vs working code [ In reply to ]
On Mon, 2 Oct 2023 at 09:10, Barry via Python-list
<python-list@python.org> wrote:
>
>
>
> > On 1 Oct 2023, at 19:36, Richard Damon via Python-list <python-list@python.org> wrote:
> >
> > Perhaps a better method would be rather than just using the name and catching the exception, use a real already_initialized flag (set to True when you initialize), and look it up with getattr() with a default value of False.
> I would use a class variable not an instance variable.
>
> class OnlyOne:
> sole_instance = None
> def __init__(self):
> assert OnlyOne.sole_instance is None
> OnlyOne.sole_instance = self
>

Agreed, except that this should be an if-raise rather than an assert.

ChrisA
--
https://mail.python.org/mailman/listinfo/python-list
Re: type annotation vs working code [ In reply to ]
On 02/10/2023 00.57, Karsten Hilbert via Python-list wrote:
> Sorry for having conflated the core of the matter with all
> the Borg shenanigans, that's where I found the problem in my
> real code, so there :-)

The first question when dealing with the Singleton Pattern is what to do
when more than one instantiation is attempted:

- silently return the first instance
- raise an exception


The 'problem' interpreting the original code was that the 'Borg
Pattern', is not limited in number, but is where some class-attribute
list (or dict) is used to enable all instances to be aware of each of
the others (IIRC).

Is choosing names as important as selecting/implementing smart algorithms?


> Consider this:
>
> #----------------------------------------------------
> class Surprise:
> def __init__(self, with_type_annotation=False):
> if with_type_annotation:
> try:
> self.does_not_exist:bool
> print('does_not_exist does exist')
> except AttributeError:
> print('does_not_exist does not exist')
> return
>
> try:
> self.does_not_exist
> print('does_not_exist does exist')
> except AttributeError:
> print('does_not_exist does not exist')
>
> Surprise(with_type_annotation = False)
> Surprise(with_type_annotation = True)
> #----------------------------------------------------
>
> Is this how it is supposed to be ?

Wasn't this answered earlier? (@Mats)

That self.does_not_exist:bool isn't interpreted by Python to mean the
same as self.does_not_exist.


>> ...and so we're addressing the important question: the try-test is for existence, cf for
>> some value.
>>
>> This can also be achieved by using the attribute in a legal expression, eg:
> ...
>> Might this remove the confusion (ref: @Mats):
>>
>> self.already_initialized:bool == True
>
> Not for me as that would _create_ already_initialized on the
> instance. It would not allow me to test for it.
>
>>> Which seems akin constructs for generating compatibility
>>> between versions.
>>
>> versions of ?
>
> Of the language. Sometimes one tests for existence of a given
> class in a module and defines said class oneself if it does
> not exist. But that's leading astray.
>
>> What is the intent: a class where each instance is aware of every other instance - yet
>> the word "Singleton" implies there's only one (cf a dict full of ...)?
>
> The latter.

and so, returning to the matter of 'readability':

- the name "Borg" de-railed comprehension

- _instances:dict = {} implied the tracking of more than one

- should the class have been called either;

class SomethingSingleton():

or a Singleton() class defined, which is then sub-classed, ie

class Something( Singleton ):

in order to better communicate the coder's intent to the reader?

- from there, plenty of 'templates' exist for Singletons, so why do
something quite different/alien to the reader?
(thus concurring with @Richard: "tricky" subverts 'readability')

- is it better to use a technique which 'we' will recognise, or to ask
'us' to comprehend something 'new'?
(unless the 'new' is taking-advantage of a recent extension to the
language, eg switch; to justify 'trail-blazing' a
new/improved/replacement 'best practice')

- part of the 'tricky' seems to be an attempt to assess using an
instance-attribute, rather than a class-attribute. If the :bool (or
whichever) typing-annotation is added to a class-attribute (eg
_instance), will the problem arise?

- does the sequence

_instance = False
...
if not cls._instance:
ie the explicit version
if cls._instance == False:

measure 'existence' or a value?

- does the sequence

_instance = None
...
if not cls._instance:
ie the explicit version:
if cls._instance is None:

measure 'existence' or identity?
(indeed, are they more-or-less the same concept?)

- do the *attr() functions test for 'existence'?

(that said, most of the code-examples I spotted, in reading-up on this,
use either None or False - et tu Brute!)


Speaking of reading-up:

- am wondering where PEP 661 - Sentinel Values is 'going'?

- this article (https://python-patterns.guide/gang-of-four/singleton/)
mentions that the original GoF Singleton Pattern preceded Python
(particularly Python 3 classes). Also, that Python doesn't have
complications present in C++. It further discusses "several drawbacks",
which also champion 'readability' over 'trick' or 'sophistication'. I
think you'll enjoy it!

--
Regards,
=dn
--
https://mail.python.org/mailman/listinfo/python-list
Re: type annotation vs working code [ In reply to ]
On Wed, 4 Oct 2023 at 15:27, dn via Python-list <python-list@python.org> wrote:
> - should the class have been called either;
>
> class SomethingSingleton():
>
> or a Singleton() class defined, which is then sub-classed, ie
>
> class Something( Singleton ):
>
> in order to better communicate the coder's intent to the reader?

TBH, I don't think it's right to have a Singleton class which is
subclassed by a bunch of different singletons. They aren't really
subclasses of the same class. I could imagine Singleton being a
metaclass, perhaps, but otherwise, they're not really similar to each
other.

ChrisA
--
https://mail.python.org/mailman/listinfo/python-list
Re: type annotation vs working code [ In reply to ]
On 4/10/23 5:25 pm, dn wrote:
> The first question when dealing with the Singleton Pattern is what to do
> when more than one instantiation is attempted

My preferred way of handling singletons is not to expose the class
itself, but a function that creates an instance the first time it's
called, and returns that instance subsequently. The problem then
doesn't arise.

--
Greg

--
https://mail.python.org/mailman/listinfo/python-list
Re: type annotation vs working code [ In reply to ]
On Wed, 4 Oct 2023 at 17:47, Greg Ewing via Python-list
<python-list@python.org> wrote:
>
> On 4/10/23 5:25 pm, dn wrote:
> > The first question when dealing with the Singleton Pattern is what to do
> > when more than one instantiation is attempted
>
> My preferred way of handling singletons is not to expose the class
> itself, but a function that creates an instance the first time it's
> called, and returns that instance subsequently. The problem then
> doesn't arise.
>

That's one option. Personally, I don't use them very much, but if I
do, it's usually actually as a class that never gets instantiated:

class PileOfAttributes:
x = 1
y = 2
spam = "ham"

ChrisA
--
https://mail.python.org/mailman/listinfo/python-list
Re: type annotation vs working code [ In reply to ]
On 04/10/2023 19.41, Chris Angelico via Python-list wrote:
> On Wed, 4 Oct 2023 at 15:27, dn via Python-list <python-list@python.org> wrote:
>> - should the class have been called either;
>>
>> class SomethingSingleton():
>>
>> or a Singleton() class defined, which is then sub-classed, ie
>>
>> class Something( Singleton ):
>>
>> in order to better communicate the coder's intent to the reader?
>
> TBH, I don't think it's right to have a Singleton class which is
> subclassed by a bunch of different singletons. They aren't really
> subclasses of the same class. I could imagine Singleton being a
> metaclass, perhaps, but otherwise, they're not really similar to each
> other.

I'm with you on this - should have made Singleton() an ABC.

Yes, would only be a skeleton around which concrete singleton classes
could be built.

Like you, I v.rarely use them - but which means that the ABC is useful
because it would save me from having to remember the curly-bits
all-over-again...

--
Regards,
=dn
--
https://mail.python.org/mailman/listinfo/python-list
Re: type annotation vs working code [ In reply to ]
Am Wed, Oct 04, 2023 at 05:25:04PM +1300 schrieb dn via Python-list:

> The first question when dealing with the Singleton Pattern is what to do when more than
> one instantiation is attempted:
>
> - silently return the first instance

This, in my case.

> and so, returning to the matter of 'readability':
>
> - the name "Borg" de-railed comprehension
>
> - _instances:dict = {} implied the tracking of more than one

Child classes, yes, each being a Singleton.

> or a Singleton() class defined, which is then sub-classed, ie
>
> class Something( Singleton ):

Could have been but the legacy codebase came with Borg ...

> - from there, plenty of 'templates' exist for Singletons,

... which was taken from the Web ages ago.

> - this article (https://python-patterns.guide/gang-of-four/singleton/)

Reading.

Karsten
--
GPG 40BE 5B0E C98E 1713 AFA6 5BC0 3BEA AC80 7D4F C89B
--
https://mail.python.org/mailman/listinfo/python-list