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
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
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
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
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
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
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-defini
