type annotation vs working code
dn
PythonList at DancesWithMice.info
Sun Oct 1 00:41:29 EDT 2023
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
More information about the Python-list
mailing list