• type annotation vs working code

    From Karsten Hilbert@21:1/5 to All on Sat Sep 30 21:00:40 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Karsten Hilbert on Sat Sep 30 19:30:41 2023
    Karsten Hilbert <Karsten.Hilbert@gmx.net> writes:
    Where's the error in my thinking (or code) ?

    Boiling down your code to the source file

    name:bool
    print( name )

    , the "name:bool" specifies the type of the name, but does
    not actually bind the name to a value nor does it require the
    name to have a value, so one gets an error only on the second
    line. Maybe this "feature" is somewhat underdocumented.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Mats Wichmann@21:1/5 to Karsten Hilbert via Python-list on Sat Sep 30 13:34:52 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dn@21:1/5 to Karsten Hilbert via Python-list on Sun Oct 1 09:04:05 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Karsten Hilbert@21:1/5 to All on Sun Oct 1 00:25:11 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dn@21:1/5 to Karsten Hilbert via Python-list on Sun Oct 1 17:41:29 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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Karsten Hilbert@21:1/5 to class in a module and defines on Sun Oct 1 13:57:10 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Angelico@21:1/5 to python-list@python.org on Sun Oct 1 23:21:00 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Damon@21:1/5 to All on Sun Oct 1 14:33:21 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Barry@21:1/5 to All on Sun Oct 1 23:08:52 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Angelico@21:1/5 to python-list@python.org on Mon Oct 2 09:21:42 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dn@21:1/5 to Karsten Hilbert via Python-list on Wed Oct 4 17:25:04 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Greg Ewing@21:1/5 to All on Wed Oct 4 19:44:19 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Angelico@21:1/5 to dn via Python-list on Wed Oct 4 17:41:24 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Angelico@21:1/5 to python-list@python.org on Wed Oct 4 17:49:44 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dn@21:1/5 to Chris Angelico via Python-list on Wed Oct 4 21:05:29 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to PythonList@DancesWithMice.info on Wed Oct 4 11:56:50 2023
    dn <PythonList@DancesWithMice.info> writes:
    - should the class have been called either;
    class SomethingSingleton():
    or a Singleton() class defined, which is then sub-classed, ie

    A singleton is a single instance of a class that usually is
    used "globally" in an application. Such as "root" in

    import logging

    root = logging.getLogger()

    . So, when a singleton is needed in Python, I would follow
    the model of "getLogger". You do not have to put in effort
    to keep any client from creating more than one instance of
    a class. Multiple instances of a class intended for singletons
    might help when one is writing tests for this class.

    (My above opinion was stolen from Brandon Rhodes.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Karsten Hilbert@21:1/5 to All on Wed Oct 4 23:38:40 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)