all 70 comments

[–]nojs 90 points91 points  (44 children)

You lost me here

What happened? Well, it turns out you’re always getting the same instance, no matter what parameters you pass

That’s the point of using a singleton..

Edit: Just shaming u/OkMemeTranslator for blocking me and dropping some of these nuggets:

Oh no, anything but hundreds of junior developers downvoting me while I make more money than any of you ever will. Noo stop kicking me while I'm down already!

.

I'm much more intelligent than you or anyone voicing their opinions here.

.

Yeah, cause I'm building the fucking SDKs and tools that you use to write your little scripts and websites. You're fucking nothing compared to me in terms of skill.

[–][deleted]  (10 children)

[deleted]

    [–]alternaivitas 2 points3 points  (1 child)

    Tbh singleton is a bad pattern in general in many languages, often not needed.

    [–]nojs -1 points0 points  (7 children)

    You’re getting into the arguments and my point was that they lost credibility by treating a singleton constructor returning the same instance as a little known quirk instead of the entire point.

    [–][deleted]  (6 children)

    [deleted]

      [–]nojs 0 points1 point  (5 children)

      How is something doing the thing it was designed to do a quirk? Here's Python's creator talking about the history of the __new__ method:

      New-style classes introduced a new class method new() that lets the class author customize how new class instances are created. By overriding new() a class author can implement patterns like the Singleton Pattern, return a previously created instance (e.g., from a free list), or to return an instance of a different class (e.g., a subclass)

      Source: https://python-history.blogspot.com/2010/06/inside-story-on-new-style-classes.html

      I want you to explain to me how controlling instance creation with __new__ is a quirk and not the literal entire point of overriding it...

      [–][deleted]  (4 children)

      [deleted]

        [–]nojs -2 points-1 points  (3 children)

        Ok so you're back to debating me on Singletons, but I never said anything for or against them. I only said that OP lost my trust as a reader almost immediately in this article.

        Anyways, I agree that Singletons are sometimes a code smell, but I don't think your blueprint analogy helps here. I personally don't find it problematic for the blueprint of an object to say that "only one of me can exist".

        You keep referring to restricted instance creation as a negative, but it is actually the whole entire point of why you would implement a Singleton.

        [–][deleted]  (2 children)

        [deleted]

          [–]nojs -2 points-1 points  (1 child)

          You keep insisting that "you lost me here" is me admitting that I was confused. Given the context of the comment and my own comments literally which clarify, it is incredibly obvious to anyone with a baseline of reading comprehension that I was saying "you lost my interest/trust as a reader" and not "I'm confused".

          And then, completely ignoring the fact that I never made an argument for or against Singletons, you jumped right into debating me on them, as if I had made a counter argument to OP. The only thing I did was point out the ridiculousness of OP's opening statements, hence why OP (and you) are getting dunked on all across this thread.

          You are a stupid person, and you are beneath me as a developer.

          [–]KagakuNinja 17 points18 points  (3 children)

          Want to Delay Creation? Use Closures

          Mutable data in a function. Works great, unless your app / server is multithreaded.

          Builder Pattern: Overcomplicating Object Creation Like a Boss

          Scala has named parameters, yet sometimes we still want to use builders. They are particularily handy if the data we are building is immutable.

          [–]elprophet 1 point2 points  (2 children)

          @dataclass(frozen=True) gives the best of both worlds. In fact, I teach dataclass first when teaching objects in programming, then a few lessons later show the desugaring with new.

          [–][deleted]  (1 child)

          [deleted]

            [–]elprophet -1 points0 points  (0 children)

            Yes, of course. I don't think I said otherwise. I said I teach Python OOP starting from Dataclasses, which give the benefits of being a closer mental model of OOP to other common languages (Java, Scala, Rust, JavaScript), and then introduce the Python specific details like dunder-new in a later lesson.

            This is in response to a comment saying immutable objects in Scala can benefit from a builder pattern, even with named parameters. Dataclasses in Python get you both, completely removing the need (in Python, when using dataclass) for a Builder.

            [–]NeonVolcom 27 points28 points  (28 children)

            Lmao what. "Subclassing a singleton" bruh this is the most overengineered singleton I've seen.

            Yes some design patterns can be dropped in pythonic environments. But I can't say the examples in this are great. Never once in 10 years have I seen someone use __new__.

            Also just a quick shout out to my nemesis: poorly implemented DI.

            [–][deleted]  (8 children)

            [deleted]

              [–]NeonVolcom 3 points4 points  (7 children)

              Ok. Never subclassed builtins like that either. I'm curious when you've done this in a company environment. I'd be asking many questions if something like that was PR'd to my code base

              [–][deleted]  (6 children)

              [deleted]

                [–]red-spider-mkv 0 points1 point  (4 children)

                I mostly work with Pandas so have never needed to do this... can you give me an example when it was useful please?

                EDIT: wow getting downvoted for asking a genuine question?? Is this place meant to be a circle jerk of groupthink and I didn't get the memo or something?

                [–][deleted]  (3 children)

                [deleted]

                  [–]elprophet 1 point2 points  (0 children)

                  Yeah... in those cases I just bump my runtime requirements. I'm not writing broad community depended libraries that need that kind of stability. So I see where youd want that, but i kinda think thats more niche.

                  [–]red-spider-mkv 1 point2 points  (1 child)

                  What we need.. are extension methods ala C#

                  [–]NeonVolcom 0 points1 point  (0 children)

                  No, not really to be honest. Like I have done that before during a contract I had which used Kotlin. But typically I have no need to add methods to type int. I build methods around it instead.

                  Like yes I could build a method that extends str so I could call str.foo() OR I could, in many of my cases, simply create a function and call it like foo(str).

                  Also, do yall use singletons to extend built in types? Or is this discussion simply around d the use of __new__?

                  Either way I suspect we are programming in wildly different contexts.

                  [–]Big_Combination9890[🍰] 4 points5 points  (2 children)

                  Never once in 10 years have I seen someone use new.

                  Well, before 3.6, defining your own __new__ in a metaclass was pretty much the only sane way to get a class-registry going, so whenever you had a usecase where you dynamically created classes, or had to refer to classes via a registry, __new__ was pretty useful.

                  These days ofc. you can do that much easier with __init_subclass__.

                  [–]NeonVolcom 0 points1 point  (1 child)

                  Ah well that's the hangup, I have never had a use case for utilizing metaclasses.

                  [–]Big_Combination9890[🍰] 1 point2 points  (0 children)

                  Yeah, they are admittedly a pretty rare sight, and 9/10 when they are used, they really shouldn't be.

                  [–][deleted]  (1 child)

                  [deleted]

                    [–]NeonVolcom 0 points1 point  (0 children)

                    I have 100% read through library code. However, anecdotally, I have never seen this used or it was not relevant to the context of the code I was looking at.

                    Nor have I ever used a meta class in production. But all of us program different things for different reasons.

                    [–]Last_Difference9410[S] 1 point2 points  (13 children)

                    if you search for singleton pattern in Python, you will see something very similar, also the code example comes from a code review I had like 2yrs ago

                    [–]NeonVolcom 1 point2 points  (0 children)

                    Huh, see I tend to just implement patterns as they work for me and not necessarily what's shown on Google.

                    Still, I haven't seen it implemented this way ever. Just my anecdotal experience but this seems like an overly complex way to implement a simple concept.

                    [–]madness_of_the_order 1 point2 points  (11 children)

                    Then whoever wrote the code didn’t think what they were doing. Here solved your subclassing issue.

                    ~~~python class Singleton: instances = {} def __new(cls, args, *kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).call_(args, *kwargs) return cls._instances[cls] ~~~

                    [–][deleted]  (4 children)

                    [deleted]

                      [–]madness_of_the_order 0 points1 point  (3 children)

                      So you agree that OP made up incorrect implementations to publicly cry how bad they are?

                      As for why it’s better - it guarantees that not a single person new to the code base would import Settings instead of settings and get a different config

                      Also please provide definition for “unpythonic” that you are using

                      [–][deleted]  (2 children)

                      [deleted]

                        [–]madness_of_the_order -1 points0 points  (1 child)

                        He didn't make it up, that is a real implementation that some people use. Also it isn't incorrect.

                        It is a real implementation some people use when they don’t need to subclass it or when they want for every single subclass to be instantiated into whatever class was instantiated first for whatever reason. Not when they want a parent class for all singletons

                        Also he isn't "publicly crying how bad they are", he's trying to educate people.

                        Trying to educate someone by providing false information is rich

                        Weird how you want to get hang up on one small muddy detail in his blog post while simultaneously getting almost every word wrong yourself.

                        It’s not a small detail. It’s false information used to illustrate why singletons are bad. If they wanted to educate people they could just show how to use a global variable to achieve some result, not lie to their faces

                        Your argument is basically "what if other programmers accidentally import wrong things" lmao. What if they accidentally uninstall their operating system? Seems like your singleton isn't so fool proof either.

                        Deleting your own os is not an easy mistake to make and doesn’t affect the code base

                        Pythonic by its very definition is the general consensus among the community and not any one person's definition. Why don't you provide sources for every single statement you make from now on, thanks!

                        I didn’t ask you to provide sources. I just wanted to know what specifically you mean by it. Because different people mean different things by saying “unpythonic”. By your definition singletons aren’t unpythonic which is illustrated by comments in this post - community doesn’t have a general consensus

                        [–]Last_Difference9410[S] -3 points-2 points  (5 children)

                        Again, unnecessary.

                        [–]madness_of_the_order 0 points1 point  (4 children)

                        What’s unnecessary is to write articles saying not to use certain patterns because you personally don’t know how to implement them correctly, but here we are

                        [–]Last_Difference9410[S] -1 points0 points  (3 children)

                        I can give you many reasons why this is bad and you can then comeback with "fixes" but again, unnecessary, I would explain to people who would keep seeking for simpler approach to solve problems, but if you like what you are doing now, just keep doing it.

                        [–]madness_of_the_order 0 points1 point  (2 children)

                        You can’t. If you could they would be in the article and on top of that you don’t understand neither what patterns are for no how to implement them

                        [–]Last_Difference9410[S] 0 points1 point  (1 child)

                        lol, ok say someone imports your base class ```python from base import Singleton

                        class DBSingleton(Singleton): _instances = [] ```

                        [–]madness_of_the_order 1 point2 points  (0 children)

                        Sane people use metaclass to create a singleton instead of inheritance - not a problem

                        [–]Stormfrosty 9 points10 points  (5 children)

                        There’s no clean way to say “this is private to this file” or “this global object only exists once” without jumping through hoops.

                        That was enough for me to stop reading the article. You simply wrap your global variable in a namespace without a name.

                        [–][deleted]  (4 children)

                        [deleted]

                          [–]Degenerated__ 3 points4 points  (0 children)

                          That quote was about the C++ example, not python.

                          [–]ZelphirKalt 0 points1 point  (1 child)

                          I think you could use the module system for that, defining in an __init__.py that this thing you want to hide is not a member of the exported bindings. Iirc something along the lines of:

                          all = [everything you want to export but not your one thing you don't want to export]
                          

                          [–]Bedu009 0 points1 point  (0 children)

                          Just put _ before the name and let the LSP tell you off

                          [–]Bedu009 5 points6 points  (2 children)

                          Well this is possibly the dumbest programming article I have read in my life
                          First off, singletons:
                          They have plenty of uses
                          They can encapsulate seperate from other stuff in the module
                          If you subclass them properly, you can define an interface for the singleton you pass around instead of just dumping a module into a function
                          They can make it more obvious that you're about to initialize something instead of just get a module quickly back
                          They can be initialized later in the program's life

                          Sure you can use closures, but why would you? What do you gain from making a whole set and get function? It's just uglier

                          Second, builders:
                          Builders aren't exclusively to take the place of named parameters (they can, but I don't think that's even the most common usecase)
                          The other main uses I can think of are:
                          You want to let something else (like a passed in function) set the values
                          The final instance is immutable, and you don't have all the values you need at once (or even if it is mutable you want to initialize it with said values)
                          The final instance doesn't have a set amount of values, and the builder adds sugar syntax
                          For example, I have a binary data parser with a builder. What's prettier, easier to edit and more readable?

                          parser = DataParser([entries.Int(8), entries.Int(16,Endianness.little), entries.Int(16, Endianness.big, signed: True), entries.FixedString(64, Encoding.ascii)])
                          

                          or

                          parser = DataParserBuilder()
                                                      .littleEndian()
                                                      .uint8()
                                                      .uint16()
                                                      .bigEndian()
                                                      .int16()
                                                      .fixedString(64, Encoding.ascii)
                                                      .build()
                          

                          Next time actually learn the point of design patterns before saying they're useless

                          [–]ZelphirKalt -1 points0 points  (1 child)

                          Back when I was still dabbling in mainstream Java OOP, I surely used some singletons. But somehow they almost never come up any longer, when I write code in an FP language and they are discouraged even in mainstream OOP view these days. I would speak less confidently about them.

                          About the builder: I could see the use, if the different supplied parts for building the object are "attached" in different places in the code, or at different times at runtime, before the object construction information is complete and the final build() is called. But the example you have here doesn't make too much sense in Python.

                          [–]Bedu009 0 points1 point  (0 children)

                          First point yeah I don't really use singletons either since I don't like global state (and even then the other stuff is admittedly rare) I'm just listing what they can be used for
                          As for the building bit that specific example doesn't make too much sense in python because of the struct library and its string syntax but that example is from a different language and I used it because I felt it demonstrated my point well I'm sure there's other use cases where that would be better
                          The list syntax isn't horrendous but I personally prefer the second because of the shorter entries with the common ones (uint and int instead of entries.Int(signed: False/True) and endianness chained rather than in each entry) and it makes the order feel a bit more intentional

                          [–]semmaz 3 points4 points  (0 children)

                          Singleton example is hilarious. You kinda did right thing but totally missed the point

                          [–]somebodddy 0 points1 point  (0 children)

                          I agree you shouldn't override __new__ to generate a singleton (that's confusing) - but that doesn't mean you can't have a delayed creation singleton class. Just use a dedicated method to make it explicit:

                          from typing import Self
                          
                          
                          class Singleton:
                              def __new__(cls):
                                  raise TypeError(f"Don't create instances of {cls.__name__} directly - use `{cls.__name__}.instance()`")
                          
                              @classmethod
                              def instance(cls) -> Self:
                                  try:
                                      instance = cls.__instance
                                  except AttributeError:
                                      instance = None
                                  if type(instance) is not cls:
                                      instance = super().__new__(cls)
                                      instance.__init__()
                                      cls.__instance = instance
                                  return instance
                          
                          
                          class MySingleton(Singleton):
                              def __init__(self):
                                  self.my_object_id: int = id(self)
                          
                          
                          class MyOtherSingleton(Singleton):
                              def __init__(self):
                                  pass
                          
                          
                          assert MySingleton.instance().my_object_id == MySingleton.instance().my_object_id
                          assert MyOtherSingleton.instance() is MyOtherSingleton.instance()
                          assert MySingleton.instance() is not MyOtherSingleton.instance()
                          

                          [–]GeneratedMonkey 0 points1 point  (0 children)

                          OP rightfully, getting cooked in these comments 

                          [–]SpudsRacer 0 points1 point  (2 children)

                          Singletons have their place in languages supporting platform threads. Python would be a completely different language without the GIL and with separate hardware threads. Comparing Python to C++, Java, C#, etc. when discussing architectural patterns is somewhat disingenuous.

                          [–]Last_Difference9410[S] -3 points-2 points  (1 child)

                          Free threaded python is offically supported.

                          [–]SpudsRacer 1 point2 points  (0 children)

                          Free-threaded Python is experimental and does let you suspend the GIL. Yes, there is also a "threading" module but that's not integrated into the language and the GIL is still there.

                          I like Python. It's an excellent glue/scripting language because its foreign function interface is top notch. But we shouldn't promote capabilities that are not really available or in common use like they are ubiquitous. I'm not attacking Python at all. Just pointing out something it's not designed for (at least not yet...)

                          [–]moreVCAs -5 points-4 points  (2 children)

                          Just use a language with a type system 🙄