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

Reply via email to