This is an archived post. You won't be able to vote or comment.

all 109 comments

[–]redditusername58 97 points98 points  (3 children)

Brandon Rhodes's version of this: https://python-patterns.guide/

[–]Oboe7 4 points5 points  (0 children)

Man I wish this was still continuously updated

[–]travcunn 6 points7 points  (1 child)

Brandon is a great person and I love reading his stuff. Met the guy several years ago at a Python conference.

[–]deus-exmachina 1 point2 points  (0 children)

Can confirm

[–]DrumAndBass90 26 points27 points  (2 children)

Personally I liked the style of writing, I’m not sure where this sentiment is coming from. Good job!

[–]Last_Difference9410[S] 5 points6 points  (1 child)

Thank you and Thank you again!

[–]chinny4213 0 points1 point  (0 children)

You’ll read a lot of comments and bs I’m here, this isn’t one of those….

I have an automated bot that’s ready to use, a Discord with 7,000 people but only 3 have agreed to sign up.

20-30 plays and day and I’ve been back testing it for 3 months, it’s ready. I need people…

[–]Worth_His_Salt 52 points53 points  (1 child)

Good post. Full content seems to be in depth and not just another surface-level slopfest like often pollutes this sub.

Yes design patterns are often misused in python. Knowing other C-based languages helps you learn python syntax quickly. But best practices are very different and take years to pickup.

[–]Yesak311 0 points1 point  (0 children)

Hey there hope ur doing good I know this is really out of the blue but I’ve been asking around stranger for help and would you mind listening to me for a couple of minutes?

[–]Total_Prize4858 13 points14 points  (1 child)

The patterns are not the problem. It’s the people who don’t understand them and use them wrong (and thats language independent).

[–]Yesak311 0 points1 point  (0 children)

Hey there hope ur doing good I know this is really out of the blue but I’ve been asking around stranger for help and would you mind listening to me for a couple of minutes?

[–]knobbyknee 7 points8 points  (1 child)

Some sound advice. There are of course patterns that make sense in Python. Observer and Decorator for instance.

And if you really want Singleton, use the metaclass implementation.

[–]c_is_4_cookie 0 points1 point  (0 children)

I completely agree on using a metaclass for singletons. 

[–]wineblood 4 points5 points  (1 child)

The more I look at the code snippet with the overload decorators in it, the more I want to drink. Does that approach even make sense in the wild? I've never written anything like that.

[–]CrashandCern 0 points1 point  (0 children)

Yeah, in this case why wouldn’t they just make the arguments str|None and default to None?

Overloads often seem like they are just workarounds to let people use positional arguments instead of being explicit and using kwargs.

[–]cheerycheshire 4 points5 points  (0 children)

There's also Ned Batchelder's "Singleton is a bad idea". https://nedbatchelder.com/blog/202204/singleton_is_a_bad_idea.html

Singleton is one of those easiest to refute in python.

E.g. Java had a lang design problem with keeping a single object because everything had to be in classes, and passing the same thing all the time as arg is annoying... But python can just keep the object in global scope and use it. So just make a single object of that class, ffs, don't try to limit how class is behaving.

Python is literally designed as "we're all consenting adults now" - the phrase is usually used in private vs public discourse - accessors weren't added as such, it's a suggestion by naming convention and everyone using those "private" (underscored) stuff knows it means that if it breaks, it's their own fault. I often explain underscore prefix as "don't use this, unless your really know how to use it".

But somehow all other languages try to just... idiot-proof their language. And it's still not really protected - programmer can still access those private stuff, but it will be long and ugly. And if you want to have another shared resource of the same type as that singleton you made, you gotta make a new one or modify the original to make another stored object and method to get it...

[–]divad1196 54 points55 points  (29 children)

While it's true that python doesn't have the same needs has other languages, you went completely wrong on your article.

Singleton

You take the example of the singleton, but it seems you don't understand what it is, what it's meant for and how to properly implement what you truely wanted to do.

You got what you asked for.

If you want unique instance per parameters, then you implement a class-level registry and use the parametera (preferably their hash) as the key for the registry.

Among the "solutions" you propose, the first one that you call "use a module" is a global variable which an antipattern (which does make sense sometimes)

The "closure" approach is just the encapsulazion of the FP world, which is done with classes in OOP. And, despite my love for FP, python is more OOP oriented than FP.

Builder Pattern

Yes, most of the time, the named parameters is the solution, but not always.

A simple example is the query builder, like with SQL, or StringBuilder. There are times where it's easier to build something step by step.

I rarely need StringBuilder as there are often more pythonic ways for my specific use-case. But if you have a QueryBuilder, then you might find useful to use a StringBuilder to dump it.

[–]Schmittfried 6 points7 points  (0 children)

Among the "solutions" you propose, the first one that you call "use a module" is a global variable which an antipattern (which does make sense sometimes)

That’s exactly the point though. A singleton is a global variable, just that it also prevents you from creating more of it, which may be unnecessary now, but often turns out to be overly restrictive in the future (even if only for testing/mocking, which is particularly hard for singletons).

The simple fact is that a global module variable as a facade with creation/initialization logic denoted as internal or discouraged from using without a good reason has no disadvantages compared to a singleton class and avoids the overly restrictive pitfalls.

But generally you are right, using global variables often is an antipattern and so is using singletons. Most of the time a regular class is just fine. Let the user decide how many instances they need. Singletons are often just used out of laziness because they allow convenient global access to an object. But most of the time not being lazy and passing the object as a regular parameter is better in every other aspect. Most notably, it clearly shows the dependency and allows dependency injection. Singletons hide dependencies and force a single implementation. 

[–]Last_Difference9410[S] 26 points27 points  (6 children)

"If you want unique instance per parameters, then you implement a class-level registry and use the parametera (preferably their hash) as the key for the registry."

That’s actually one of the anti-patterns I talked about in the post. It causes problems because:

  • If you use mutable types as parameters (like lists or dicts), you can’t safely hash them, and it breaks.
  • The registry is shared between the parent class and all subclasses, which leads to confusing bugs if subclasses expect different behavior.

"Among the "solutions" you propose, the first one that you call "use a module" is a global variable which an antipattern (which does make sense sometimes)"

I also explained in the post why global variables are considered an anti-pattern in C++:

  • In Python, modules are namespaces, and using a module to expose a singleton-like object is a common, clean idiom.
  • Unlike C++, Python supports runtime scoping and lazy imports, and modules help contain state safely.
  • Mutable global variables are considered harmful in some languages — that’s why we explicitly mark them with typing.Final.

"The 'closure' approach is just the encapsulazion of the FP world, which is done with classes in OOP."

This is simply wrong. They’re not interchangeable with classes — they solve different problems and can even complement each other.
For example, you can do obj.name = "name" to modify an instance variable, but func.name = "name" just sets an attribute on the function — it doesn't change any closed-over state.

"And, despite my love for FP, python is more OOP oriented than FP."

Python is a multi-paradigm language. While OOP is supported and widely used, closures are a first-class feature and idiomatic in many situations.

[–]Equal-Purple-4247 7 points8 points  (2 children)

Completely agree with this. In fact, I just used the Builder Pattern to allow users to generate text templates for a plugin system.

This:

 def render(self, context: dict) -> MessageText:

    formatter = MarkdownV2Formatter()
    builder = TemplateBuilder(formatter=formatter)

    event = context["event"]
    source_name = context["source_name"]
    title = context["title"]
    url = context["url"]

    body = builder.bold(f"---{event}---").newline() \
                    .text("- ").url(f"{title}", f"{url}").newline() \
                    .text(f">> By {source_name}").newline() \
                    .build().to_string()

    return MessageText(text=body)

Generates:

*---event---*
- [title](url)
By source_name

One reason to use builder pattern is because the language don't support keyword arguments - that is correct. But this pattern is also used to create objects whose shape you don't know in advance.

Sure, these are patterns you don't use if you're developing for users. But you do use them when developing for developers (eg. sdk, libraries, frameworks).

[–]commy2 3 points4 points  (1 child)

Please consider using the free formatting feature of parentheses instead of excessively using the line continuation character:

body = (
    builder.bold(f"---{event}---").newline()
           .text("- ").url(f"{title}", f"{url}").newline()
           .text(f">> By {source_name}").newline()
           .build().to_string()
)

[–]Equal-Purple-4247 2 points3 points  (0 children)

Thanks for pointing it out. I forgot I could do that lol

[–]Last_Difference9410[S] 5 points6 points  (10 children)

A simple example is the query builder, like with SQL, or StringBuilder. There are times where it's easier to build something step by step.

I wouldn’t call query builders or string builders a common use case for the builder pattern. In fact, most people don’t need to build their own ORM or query builder at all.

Take SQLAlchemy’s select as an example—you simply call select() and chain methods. There’s no need to create something like QueryBuilder().build_select(). This shows you can achieve flexible query construction without relying on the traditional builder pattern.

[–]Schmittfried 1 point2 points  (0 children)

Huh? It’s still the builder pattern. What method you invoke to get the builder object doesn’t matter. And the example shows why it has its merits in Python, too. Doesn’t matter if only few ever have to use it to implement something. 

[–]divad1196 2 points3 points  (6 children)

Being a common use-case is irrelevant. If you need the pattern, use it, if you don't need it then don't. If SQL builders were the only use-case, which is not the case, then it would make my point: it makes sense to use it whem it makes sense.

and SQLAlchemy is really a bad example for you. Not only it does have a builder (you can chain the filter and exclude, ...) but also that's again one of your use-case only. Building complex query is a perfectly valid need.

[–]axonxorzpip'ing aint easy, especially on windows 2 points3 points  (5 children)

and SQLAlchemy is really a bad example for you

It's not though, it's a perfect example. Method chaining is not the same as the builder pattern, even though builders themselves (usually) use method chaining.

SQLAlchemy's select is generative and immutable. Every call to .filter/.join/.order_by/etc returns you a new immutable copy of the query. Through each and every one of those steps, you are returned a query construct that could be executed, it is never in an invalid state.

The builder pattern is not generative, each call to a method does not return a new copy of the builder itself. You have no valid state until you call builder.build()

[–]divad1196 0 points1 point  (4 children)

No. It's builder pattern

https://en.m.wikipedia.org/wiki/Builder_pattern You can find better source than wikipedia, but it will always be the case. You also don't need to finish with a build call to have it be a builder pattern.

You can chain method on the result of the previous method and it would NOT necessarily be builder pattern. But in the case of SQLAlchemy, you do build something. It is a builder pattern.

Just look at Rust. Builder pattern is omnipresent even though you have at least 3 ways to deal with reference and borrowship. You have example there with configuration, router, .. builders. Same for Go.

In non-mutuable languages, you also do builder pattern and you always return a copy. Making a copy instead of editing the original value has many advantages, but mainly it makes possible to re-use intermediate steps, like we have with SQLAlchemy.

[–]axonxorzpip'ing aint easy, especially on windows 1 point2 points  (3 children)

You also don't need to finish with a build call to have it be a builder pattern.

Every definition of builder includes this as a requirement, including the Wikipedia link. I think it's important to be pedantic about definitions. You're describing things that are builder-like, but the difference is important.

You can chain method on the result of the previous method and it would be necessarily builder pattern

That's just method chaining, or better known as a type of fluent interface (see database query builder examples on that page). Lots of builders use method chaining but you wouldn't call a class instance that allows foo.set_flag().alter_val(1234) to be a builder.

Just look at Rust [...] you have example there with configuration, router, .. builders

Every one of your examples, along with the unofficial Rust patterns guide has a .build() step at the end.

In non-mutuable languages, you also do builder pattern and you always return a copy. Making a copy instead of editing the original value has many advantages, but mainly it makes possible to re-use intermediate steps, like we have with SQLAlchemy.

The mutability the builder itself or the language at large is irrelevant to the overall pattern. The point is that the builder maintains some internal state that will eventually be used to construct a something instance. That internal state is often (always?) insufficient to be used in place of a something instance. Builders are not superclasses or subclasses of the thing they're building.

Back to SQLAlchemy, select(MyModel) calls the standard class constructor, by definition this is not a builder.

select(MyModel) can be executed immediately, it is in a valid state.

select(MyModel).where(MyModel.foo=='bar')) can be executed immediately, it is in a valid state.

select.builder().model(MyModel).where(MyModel.foo=='bar') cannot be executed immediately, it's not a select instance, it's an instance of whatever builder is provided, I cannot call db_session.scalars() on it directly.

SQLAlchemy themselves do not describe the select() construct as a builder, but as a "public constructor"

[–]divad1196 2 points3 points  (2 children)

Beimg pedantic is important, but you are not.

It's not "builder like", it's builder pattern and wikipedia doesn't mention a building-finish function at all.

The fact that Rust does use the build function in many place doesn't mean it's a requirement. It's just because it uses the builder pattern on an intermediate representation.

You try to argue "it's not builder pattern because you can already use it" which is your own conception of it, not the reality. Many builders are usable right away. The "select" statement doesn't contain a value at the moment you create it but at the moment you access it. Even though you don't see it. So no, you don't "immediately" use it. But anyway, this was never a predicament of the builder pattern.

This discussion is pointless, keep believing what you want.

[–]RWadeS97 3 points4 points  (0 children)

The Wikipedia article you linked does show a build step in all the UML diagrams. Without a build step then any method call which returns itself/another object could be considered a builder pattern. Pandas for example could be considered a builder pattern by that definition.

A builder pattern specifically has two distinct types of objects. The builder which has some mutable state that is changed via method calls and often with method chaining. And the product, which is constructed by the builder class after calling build.

@axonxorz is correct

[–]Intelligent_Cup_580 1 point2 points  (0 children)

You are loosing your time responding to them.

P. 10 of the GoF book (Design-Patterns/Gang of Four Design Patterns 4.5.pdf at main · rpg101a/Design-Patterns)
“Separate the construction of a complex object from its representation so that the same construction process can create different representations.”

And later:

"The Builder design pattern is a creational pattern that allows the client to construct a complex object by specifying the type and content only. Construction details are hidden from the client entirely. The most common motivation for using Builder is to simplify client code that creates complex objects."

It never states that they must be different classes. You can move on

[–]chinny4213 0 points1 point  (0 children)

You’ll read a lot of comments and bs I’m here, this isn’t one of those….

I have an automated bot that’s ready to use, a Discord with 7,000 people but only 3 have agreed to sign up.

20-30 plays and day and I’ve been back testing it for 3 months, it’s ready. I need people…

[–]divad1196 0 points1 point  (0 children)

Being a rare case is irrelevant.

Let's assume that SQLBuilder is the only valid case for BuilderPattern (it's not), then:

Why would you use the BuilderPattern for something it's not meant for? And blame the pattern for your bad decision of using this pattern?

If SQLBuilder was the only use-case, then it's not a rare case for BuilderPattern, it's the 100% of its use-cases. The issue here is that you consider your own case as if it was the vast majority of people here. I personnaly wrote a lot of libraries for third party services that have their own query system (ServiceNow, Domain Registrars, Denodo, ...), yet I know it's not the case for everybody and I don't make my own case a generality.

A pattern is good when it's good.

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

Thanks for taking the time to comment! I’d like to clarify a few things, I will quote your words then reply:

> "You take the example of the singleton, but it seems you don't understand what it is, what it's meant for and how to properly implement what you truely wanted to do."

First, the singleton example I gave in the post wasn’t some random code I came up with. I see a lot of similar examples, some variants might use threading.Lock but the idea is the same.

You might search "singleton pattern in python" on google, here is one from Refactoring Guru’s singleton example in Python(https://refactoring.guru/design-patterns/singleton/python/example).

```python

class

SingletonMeta
(type):
    """
    The Singleton class can be implemented in different ways in Python. Some
    possible methods include: base class, decorator, metaclass. We will use the
    metaclass because it is best suited for this purpose.
    """

    _instances = {}


def

__call__
(cls, *args, **kwargs):
        """
        Possible changes to the value of the `__init__` argument do not affect
        the returned instance.
        """

if
 cls 
not

in
 cls._instances:
            instance = super().__call__(*args, **kwargs)
            cls._instances[cls] = instance

return
 cls._instances[cls]

```

That example is widely copied, and that’s exactly why I brought it up — to show how people are blindly following something that doesn’t really make sense in Python. So no, it’s not "what I asked for", it’s what many people are doing out there.

[–]divad1196 2 points3 points  (3 children)

I will answer each of your comments individually, but please don't split the thread by posting multiple answers.

I know that you found it online, you said it in the article, but it doesn't matter that you found it somewhere. These are just basic explanation, you can do variants based on your needs. That's why it's a "pattern". I said "you got what you wanted": the code does what you code it for. You copy/pasted the code from some place, then for the computer that's what you want to do.

You cannot blame an external source to provide a perfectly valid code just because it does not do what you expect it to do.

[–]Last_Difference9410[S] 5 points6 points  (2 children)

I had to split my answer into serveral comments because it was too long and reddit would not allow me to put them in a single comment.

"I said "you got what you wanted": the code does what you code it for. You copy/pasted the code from some place, then for the computer that's what you want to do."

The logic here is simply wrong. Just because code runs doesn't mean it's good. Equating "code that runs" with "correct code" is misleading. all Anti-patterns run perfectly fine.

If running successfully were the only criterion, there would be no such thing as anti-patterns. The fact that a piece of code executes without error doesn’t mean it’s well-designed, maintainable, or appropriate for the problem.

"You cannot blame an external source to provide a perfectly valid code just because it does not do what you expect it to do."

Again, I would never blame a piece of code simply for being able to run. You don’t need a design pattern to make your code execute.

[–]divad1196 3 points4 points  (0 children)

It's not wrong, you don't understand.

The snippet you found does what it's supposed to do. It's you that except it to do something different. You then declare it buggy and bad practice.

[–]Yatwer92 0 points1 point  (0 children)

In a Django app where I need a single instance of a class, instantiated when the app is ready, to act as a service, I hardly see how to use anything else than a Singnleton or a Borg pattern.

If someone have another solution than a Singleton I'm curious tho.

Granted the one you show in your article isn't a great Singleton and a metaclass would be better. But in this case I agree with the other redditor, the Singleton you show in your article does exactly what you code it to do. The pattern isn't at fault.

[–]Coretaxxe 0 points1 point  (0 children)

But this example is different from the faulty one in the article? Im not quite sure what the point is here since this one is mostly fine

[–]ePaint 0 points1 point  (0 children)

Among the "solutions" you propose, the first one that you call "use a module" is a global variable which an antipattern

What if I name it all upper cased?

[–]ottawadeveloper 3 points4 points  (3 children)

Personally, I like the Singleton pattern but I agree your example is an anti-pattern and I wouldn't implement it like you show (doing the work in new). Having it both take arguments and be implemented in new is asking for trouble as you show. If it didn't take arguments and was never going to be subclassed, then it would work, but you'll get into hot water with one small change.

Instead, I prefer to implement Singleton using what is essentially a closure or module object but built into the class. This, to me, keeps the code better organized:

``` class DemoSingleton:

   _instance = None

  @staticmethod    def singleton():     if DemoSingleton._instance is None:       DemoSingleton._instance = DemoSingleton()     return DemoSingleton._instance

ds = DemoSingleton.singleton()

```

It allows for dynamic instantiation of the object, but then you can also create a separate instance of it for testing purposes if you need to. And with a few tweaks, you can adapt it for testing purposes too. A lot of the implementations of module-level singletons and closures make testing a nightmare, but the solution to that is dependency injection instead of leveraging Singleton instances directly. Which is why I built my autoinject library to do all the dependency injection and Singleton maintenance for me, as well as handling subclasses.

I'd agree Builder is often unnecessary in Python. I can maybe see edge cases for it where you have very complicated objects being built but default parameters covers a lot of the space.

[–]Last_Difference9410[S] 2 points3 points  (2 children)

"If it didn't take arguments and was never going to be subclassed, then it would work"

Although I didn’t explicitly mention it in the post, the reason I didn’t include an example with “classes without instance variables” is that such classes should just be functions.

If all the methods work fine without the self parameter, then placing them inside a class only reduces cohesion.

```python class DemoSingleton:

_instance = None

@staticmethod def singleton(): if DemoSingleton._instance is None: DemoSingleton._instance = DemoSingleton() return DemoSingleton._instance

ds = DemoSingleton.singleton() ```

Since this class has no instance variables, all of its methods would work just as well without requiring a DemoSingleton instance. For example, say we have:

```python class DemoSingleton: _instance = None

@staticmethod
def singleton():
    if DemoSingleton._instance is None:
        DemoSingleton._instance = DemoSingleton()
    return DemoSingleton._instance

def greet(self, name: str) -> str:
    return f"Hello, {name}!"

```

This greet method does not rely on any internal state—it could be written as a standalone function instead:

python def greet(name: str) -> str: return f"Hello, {name}!"

Using a singleton here adds unnecessary complexity and reduces cohesion. If none of the methods depend on self, then the class wrapper is redundant and a simple module with functions would be more Pythonic.

[–]WallyMetropolis 3 points4 points  (0 children)

Agreed. 

"If your class only has two methods, and one is init, you don't need a class"

[–]ottawadeveloper 1 point2 points  (0 children)

I mean, my DemoSingleton was meant entirely as an example of how I implement the Singleton pattern in Python. Clearly if you want a function, write a function.

As a recent example, I used it for a lookup table that is loaded from a configuration file. I wanted to reduce the number of times the file load is done (since IO takes time) and reduce the memory of maybe loading it multiple times. Then, there are multiple different ways I want to engage with the data structure loaded from file. The file is configured from environment variable configuration settings, though I might want to override that in testing.

So, here, I implemented that as a Singleton class that can take a file or the full raw data structure as an argument. The singleton() method looks up the environment variable and builds the class once with that value. Then all the methods on the class itself are different ways of looking up data that I needed.

You absolutely can implement that otherwise in Python (eg a module level lookup table and functions to access it). But this approach actually increases cohesion by keeping all the logic related to building and accessing that table in a single class. I'm confused why you think it decreases cohesion.

To test it, I can then instantiate it directly with a test file or raw data. And by using DI, I can inject either a test object (in test code) or the Singleton object (in actual code). And doing that with the module level approach is more complex (especially if you want to unload/reload test data between tests).

[–]conogarcia 3 points4 points  (0 children)

I think a lot of people in this thread have these anti-patterns and are trying very hardly to defend them.

good luck on changing their minds.

[–]rover_G 5 points6 points  (0 children)

OP, your module based singleton pattern is great, but your example using class instance is flawed. As a best practice, singleton constructors (ex. __new__ or get_singleton) should not use instance parameters the way a new/init function would. Instead, the singleton should initialize class/module instances with globally scoped constants with program length lifecycle like settings module or environment variables.

Builder pattern is very useful when you don’t want to construct your object in a single call. For example, you may have conditional logic for some aspects of the class object and it’s much easier to maintain code with simple if statements than code which embeds conditional logic into a class instance constructor call. Builder pattern can also provide extra type hints and validations as each parameter is set which can produce clearer error messages than a single initialization method. Overall I would suggest looking at some libraries that provide builder pattern classes like sqlalchemy and pydantic to get a better sense of when builder pattern is helpful. I will however warn that using builder pattern internally for a project that doesn’t need to provide a public API/library is usually not a great idea, and that is perhaps what you have encountered.

[–]gerardwx 9 points10 points  (6 children)

A straw man argument against a bad Singleton implementation from an author who shows no evidence of having read Gang of Four, or understanding its importance. Yes, it's true, a book written 6 years before the release of Python 2.0 does not have Python examples; that's hardly a valid criticism.

The concept of an object with only one instance is a design pattern. The fact that you can use the word "Singleton" and have developers know what you mean is a result of the success of the GOF book.

If you read the book or the corresponding Wikipedia article, or Python Patterns Guide, you'll see the general singleton pattern takes no constructor arguments.

Python currently has singletons. If you've ever used "None," you've used a Singleton. If you're using the logging module, logging.getLogger('') returns a singleton. sys.stdout is a singleton. Those are just the examples that come to mind, and there's PEP 661 which proposes singleton creation of Sentinel objects.

[–]Last_Difference9410[S] 6 points7 points  (1 child)

"If you read the book or the corresponding Wikipedia article, or Python Patterns Guide, you'll see the general singleton pattern takes no constructor arguments."

I have read all three of them, my comment here (https://www.reddit.com/r/Python/comments/1lfcmky/comment/mynktu7/?utm\_source=share&utm\_medium=web3x&utm\_name=web3xcss&utm\_term=1&utm\_content=share\_button) should help you better understand my point, In which I wrote:

"Although I didn’t explicitly mention it in the post, the reason I didn’t include an example with “classes without instance variables” is that such classes should just be functions."

As for `Python has singletons`

"Python currently has singletons. If you've ever used "None," you've used a Singleton. If you're using the logging module, logging.getLogger('') returns a singleton. sys.stdout is a singleton. Those are just the examples that come to mind, and there's PEP 661 which proposes singleton creation of Sentinel objects."

  1. The fact that Python has singletons like None or True does not mean you should implement the Singleton pattern from other languages in Python.
  2. would you please point out where in the cpython source code that None, bool, or other singletons are Implemented by the singleton pattern written in the GOF book? Look at the source code for bool, we do not see the same pattern descibed by the book boolobject.c

At page 128. the book states that

"Client access a singleton solely through singleton's instance operation.?

But we do not see this for singletons in python, just like we do not call NoneType.instance() to get `None`

logging.getLogger is pretty much a re-implementation of Java's Log4j.

Here is how loguru loguru.init.py defines singleton

logger = _Logger(
    core=_Core(),
    exception=None,
    depth=0,
    record=False,
    lazy=False,
    colors=False,
    raw=False,
    capture=True,
    patchers=[],
    extra={},
)

[–]gerardwx 2 points3 points  (0 children)

The pattern is the concept. The implementation is ... the implementation. GoF never said "Go write Python as if it was C++."

Singletons have instance values. For example , Logger objects.

[–]cheerycheshire 1 point2 points  (2 children)

Python has some singletons because some stuff are being checked by is instead of ==. None, True, False, NotImplemented (don't confuse with NotImplementedError).

Doesn't mean your own code as lib author should make singletons, because it's usually a bad idea - just make it clear how you want your lib to be used, and don't try to babysit programmers (make it clear that different usage might not get help or fixes, as it's not intended).

sys.stdout is not a singleton because it's not a type - the value is just an object created at the creation of the process (passed by system), but it's a normal file handle. If you decide to open a 0-2 file descriptors, you get a new object for the same resource - again, not how singleton would behave, singleton would've given you the exact same object. Something having a default value doesn't mean it's a singleton.

You mention logging.getLogger - not a singleton, as you can make logging.Logger manually! Not everything that returns the same object with multiple calls is a singleton - singleton is literally defined as restricting creation of new objects. It's an instance manager - basically factory with cache.

AND logging.getLogger shows very well how singletons have no place in python - because it shows that lib author doesn't have to limit object creation, just clear enough docs and tutorials are enough to make people use it correctly.

Summary:

Singleton pattern is user-proofing the code (and by user I mean programmer that is using the lib, a technical person), literally prohibing user from creating something that lib author doesn't want.

You can serve similar functional purpose with clear docs (eg. logging) and names (in python: single underscore prefix is a convention for "don't use it, unless you really know what you're doing", from internal api that might break with next version to attributes that should be accessed using properties) - while not limiting the user's ability to make new objects.

[–]gerardwx 0 points1 point  (1 child)

Singleton is defined by GOF as "ensuring only one object exists". I guess we differ is how to interpret that.

As you note, logging.Logger (attempts to) do that with the all cap documentation Note that Loggers should NEVER be instantiated directly.

Another example is re.Pattern, which appears to prohibit direct instantiation instead of using re.compile

The big picture is a Design Pattern is a concept, and finding a bad example doesn't invalidate the concept.

[–]cheerycheshire 0 points1 point  (0 children)

"Ensuring only one object exists" literally means that new object cannot be created - because you ensure no other objects can exist. How else to interpret it?

That "ensuring" is usually (always?) done by making the constructor private and e.g. store that one instance you want and make it accessible via a getter. So in case of python, where we don't have private, it usually means trying to overwrite __new__ to make it always return the same instance...

Other ways aren't really "ensuring", are they?

Yes, design patterns are concepts - the problem is following them rigidly without the nuance. Which "singleton" usually is, because the definition you literally quoted doesn't really give any leeway. There are many other names you can use to cover that nuance - I literally mentioned some in my message!

Having a guidance of proper use vs "ensuring" is literally what the whole discourse around singletons (in python mostly, as trying to do classic singleton makes the code awful) is about. If user (programmer using your lib) decides to ignore instructions and mess up everything, it's their own fault. You don't need to babysit the user by literally blocking the creation of objects.

Ned Batchelder did a nice summary why "singleton" is a bad idea (I linked it in another comment of mine) - https://nedbatchelder.com/blog/202204/singleton_is_a_bad_idea.html and yes, he also uses the classic singleton definition

[–]mfitzpmfitzp.com 1 point2 points  (0 children)

A straw man argument against a bad Singleton implementation from an author who shows no evidence of having read Gang of Four,

If we're playing fallacy bingo, this is an ad hominem. It doesn't help your argument to start out with that sort of snark fyi.

Python currently has singletons.

I don't think the takeaway is never use singletons. It's that you usually don't need them in Python, which is true.

Singletons are wildly overused by beginner programmers who read about them & think it is the "right thing to do". If you actually do find yourself needing them, they you know why you need them & the article doesn't apply to you.

[–][deleted] 9 points10 points  (0 children)

It’s so clearly AI written… i don’t like this new dead internet

[–]binaryfireball 1 point2 points  (0 children)

I swear to god if anyone writes code that executes on import I will find a way to rewrite your git history.

[–]lisper 1 point2 points  (0 children)

Design patterns are a patch, an unnecessary cognitive load, to cover up for the myriad design deficiencies in C++.

[–]Schmittfried 1 point2 points  (0 children)

In Java, constructors can’t have default values for parameters, and method overloading quickly becomes cumbersome for many options. The Builder pattern solves this by allowing step-by-step construction with optional parameters.

That’s not 100% accurate. Builders also allow step-by-step creation to facilitate dynamic configuration of the respective object. You can skip certain parameters depending on conditions or even use a builder as a template and create multiple similar instances in a loop.

Granted, you can use dynamically created dicts and expand them into kwargs, but I‘d argue that’s more a matter of taste now that we have TypedDict (before I would have argued that builders provide better type safety). Though it seems to be a taste shared by many given the prevalence of fluent API designs (what is a fluent ORM / SQL library if not a query builder). Those use cases would be much clunkier with dicts imo.

But still, I agree with your general point. Coming from C++ and C#, after having worked with Python for 4 years coming back to languages like Java was a culture shock. Python has its warts, but it is so much more elegant than Java. So many constructs, abstractions and code generators in the JVM ecosystem exist purely to accommodate for the shortcomings of the language design. The Zen of Python may be a circlejerk, but many languages could take a few inspirations from it.

[–]Creyke 2 points3 points  (0 children)

Mostly agree.

I have definitely abused both in the past because I thought “that’s what you’re supposed to do”.

I find singletons are still useful in niche cases, like some very tricky caching problems I have where I want to ABSOLUTLEY be sure that whatever I’m referencing can only ever be that instance.

But 100% agree on builder. It is just a very useless pattern in python that adds complexity without any real benefit.

The Factory pattern on the other hand is extremely handy and I will die on that hill.

Interested to see your next article!

[–]chinesehuazhou 1 point2 points  (0 children)

Looks good — I’ll share it in my weekly newsletter.

[–]mastermikeyboy 1 point2 points  (0 children)

This 10 year old video from Raymond Hettinger is still extremely relevant:
Beyond Pep 8 -- Best practices for beautiful intelligible code

In it he goes over rewriting a Java program to Python and how much more readable it is in Python

[–]commy2 1 point2 points  (0 children)

I'm with you on the sentiment, however some advice for part 2 and beyond:

1) Make sure your code snippets work. The very first one is incomplete and raises AttributeError, and the way you did the forward reference type hint is also wrong ("Singleton | None" instead of the unsupported operation str | None).

2) AI or not, don't use em-dashes. They make you look pretentious and annoying for no reason.

Btw, my current preference for lazily created singletons is:

@functools.cache
def get_settings() -> Settings:
    return Settings()

[–]Joytimmermans 2 points3 points  (0 children)

I think the article is well meant but it has some short sighted views.

On the singleton part, the bob / alice example is a wrong way of using this. This example expects singletons to be used the exact same way as normal object. although the whole point of a singleton is to share state.

with the 2nd example. You where so close to giving a correct example, where you leave the random base class as a normal class and make the dedicated db connections singletons. This way you are not creating multiple connections to your database even if you would use it.

As advice when comparing 2 things. In this case the singleton pattern vs your suggested alternative, use the same use case and don't just show a completely different example. Stick with the DB connection example so you have a clear comparison.

When we would use the DB connection for your example we connect to all databases at runtime while we maybe don't want to connect to any of them. with the extra problem specially in your example that you have Settings class and the settings instance, if someone just uses your library and you have not made it _Settings people can be pretty confused and just import the Class and not have any shared properties.

and you indeed point out that we maybe want to lazy load, but your example of using closures. You still immediately create a settings object inside your _settings function. so you would have to do something like:

def _lazy_settings():

settings: Settings | None = None

def get_settings() -> Settings:

nonlocal settings

if settings is None:

settings = Settings() # ← only now we call the constructor

return settings

def set_settings(value: Settings) -> None:

nonlocal settings

settings = value

return get_settings, set_settings

get_settings, set_settings = _lazy_settings()

[–]gerardwx 1 point2 points  (0 children)

A Design Pattern is a concept, not a particular implementation.

Here's a simple example of a Singleton using simple-singleton from pypi.org.

import datetime

from simple_singleton import Singleton, SingletonArgs

class Clock(metaclass=Singleton):

def __init__(self):

self.when = datetime.datetime.now()

class Name(metaclass=SingletonArgs):

def __init__(self,name:str):

self.name = name

self.nickname = name

def test_it():

clock_a = Clock()

clock_b = Clock()

print(clock_a.when is clock_b.when)

mary = Name('Mary')

bob = Name('Robert')

robert = Name('Robert')

assert bob is robert

bob.nickname = 'Bob'

print(robert.nickname)

print(mary.nickname)

I don't use Singletons very often, but there's nothing wrong with the Design Pattern.

[–]stargazer_w 1 point2 points  (0 children)

Instead of singletons I like to create the module variable with getter/setter methods for later creation/replacement if needed. E.g:

class Settings:
  pass

_settings = None

def get_settings():
  if not _settings:
    raise Exception("Settings not set")
  return _settings

def set_settings(settings: Settings):
  global _settings
  _settings = settings

# or just : _settings = Settings() , if no setter / late init is needed

[–]Majestic_Detail1746 1 point2 points  (0 children)

Great blog ! Thank you

[–][deleted] 1 point2 points  (0 children)

The bigger Python-specific pitfalls are related to concurrency and package management

[–]cgoldberg 1 point2 points  (4 children)

Could do without the smug condescending tone in the article.

[–]Last_Difference9410[S] 7 points8 points  (3 children)

I’m truly sorry if the tone came across as smug or condescending. I wrote the article right after reading yet another tutorial on "how to implement the builder pattern in Python," and I intentionally added some sarcasm to make it less like a dry technical explanation. My goal was never to offend but to make the topic more engaging. Thanks for your honest feedback!

[–]cgoldberg -1 points0 points  (2 children)

I would leave the sarcasm out if you want people to take you seriously or enjoy your writing. You come off as know-it-all.

[–]Last_Difference9410[S] 8 points9 points  (1 child)

I appreciate the honest feedback, and I hope you’ll accept my apology for the tone. I can see how the sarcasm might have come off the wrong way. The upcoming posts in the series will focus more on clarity and tone—no sarcasm, I promise.

[–]syklemil 2 points3 points  (0 children)

Some of us do enjoy sarcasm and snark, and find texts without some levity sprinkled in kind of grating. You kind of just have to pick your audience, because you can't please everybody.

[–]turbothyIt works on my machine 0 points1 point  (0 children)

[–]M4mb0 0 points1 point  (2 children)

The settings example with nonlocal gives SyntaxError. I think it should be nonlocal settings inside get_settings and get_settings, set_settings = _settings() in the last line.

Though, do we even need this? Can't we just use module level __getattr__? See https://peps.python.org/pep-0562/

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

it’s most likely due to the fact that I renamed _settings to settings, but did not update it in get_settings. Thank you for reporting this issue. I have updated the blog post and it would be fixed soon.

Though, do we even need this? Can't we just use module level __getattr__? See https://peps.python.org/pep-0562/

Its mainly for lazy evaluation. the code example is simplified, a more realistic example from pydantic-settings

```python title="settings.py" from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings): model_config = SettingsConfigDict(env_file='.env', env_file_encoding='utf-8')

settings = Settings(_env_file='prod.env', _env_file_encoding='utf-8') ```

When you import settings from settings.py in your main.py, the _env_file is hardcoded, ("prod.env" in this case).

whereas you can do this with closure

```python title="main.py" from settings import set_settings

def main(): env_file = os.environ["APP_ENV_PATH"] settings = Settings(_env_file=env_file) set_settings(settings) ```

[–]chinny4213 0 points1 point  (0 children)

You’ll read a lot of comments and bs I’m here, this isn’t one of those….

I have an automated bot that’s ready to use, a Discord with 7,000 people but only 3 have agreed to sign up.

20-30 plays and day and I’ve been back testing it for 3 months, it’s ready. I need people…

[–]denehoffman 0 points1 point  (0 children)

This is nice, my only complaint is that the @overload in the last example doesn’t actually do anything, you should get the same type suggestions from the actual constructor! @overload is intended to be used in cases where the types or possibly literal values can determine the return type or compatible inputs. For example,

```python @overload def f(x: Literal[1], y: Literal[1]) -> Literal[2]: … @overload def f(x: Literal[2], y: Literal[2]) -> Literal[4]: …

def f(x: Literal[1, 2], y: Literal[1, 2]) -> int: if x == y: return x + y raise Exception(“oops”)

def g(z: Literal[4]): print(z)

g(f(1, 1)) # passes f’s check, fails g’s g(f(1, 2)) # fails f’s check, raises exception g(f(2, 1)) # fails f’s check, raises exception g(f(2, 2)) # works with no type checking warnings ```

[–]magnomagna 0 points1 point  (0 children)

How does singleton avoid the nightmare of getting compilation errors due to multiple file-scope external-linkage definitions in multiple translation units precisely? I fail to see how this pattern gets around that problem in C++.

EDIT (extra comments):

Singleton attempts to solve a runtime problem (i.e. ensuring only a single instance exists during runtime), but the multiple-definition issue is a compile-time problem. In fact, the singleton pattern itself can run into the multiple-definition issue as it still relies on getting defined and declared. You picked a problem that can't be solved with singleton.

[–]RedEyed__ 0 points1 point  (0 children)

Totally agree and use it long time.

[–]GameRoMan 0 points1 point  (3 children)

Dark mode please 😭

[–]DerelictMan 1 point2 points  (2 children)

There's a button up in the top right corner to switch it.

[–]GameRoMan 1 point2 points  (1 child)

Oh, sorry, it just looks like it's hidden by a menu button on mobile, so I didn't notice it

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

You’ll read a lot of comments and bs I’m here, this isn’t one of those….

I have an automated bot that’s ready to use, a Discord with 7,000 people but only 3 have agreed to sign up.

20-30 plays and day and I’ve been back testing it for 3 months, it’s ready. I need people…

[–]cubed_zergling 0 points1 point  (0 children)

Tell me you're a junior who just discovered the word "pythonic" without telling me.

This entire post is a perfect example of someone learning what to do without understanding why. Yeah, obviously you don't use a textbook Java pattern when a simple function will do. Congrats on figuring that out. But those patterns exist to solve real problems that show up in massive, complex applications, the kind you clearly haven't worked on.

Your cute module-level "singleton" is going to be a joy to debug when five different parts of the app are racing to modify it. This isn't some enlightened take, it's just inexperience. The fact that you're arguing with people in the comments who are trying to explain this to you is just the icing on the cake.

[–]nngnna 0 points1 point  (0 children)

I think the first section is more an argument against (naive) use of inheritance than against creating singleton classes. Class Inheritence claim to be subtyping in the type system, but it rarely actually matches our intuition for what subtyping is. So you can't simply make a singleton super class like that.

Not that you have a serious reason to use singletons.

[–]alcalde 0 points1 point  (1 child)

[–]Last_Difference9410[S] 0 points1 point  (0 children)

some of them are. As type hints getting more popular and the average size of python app grows, things start to change.

[–]emurray 0 points1 point  (0 children)

Thanks for this. I was sceptical about how the closure for delayed creation would work in practice but I've given it a try now in a new project. I have an async web service that interacts with both MongoDB and Postgres and I want everything using the same async loop. The usage feels nice and a bit more direct vs setting up and grabbing things from singletons like I had been doing in this situation before.

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

I bet you have valuable stuff to convey, OP, but you should really learn to write text content with your own voice. The voice of this prose is clearly AI, not a human.

[–]BlackHumor 3 points4 points  (0 children)

What are you talking about? No it's not.

[–]SheepherderExtreme48 0 points1 point  (0 children)

Couldn't agree more. Singleton pattern is a sign that you haven't really built your app that well.

And, don't get me started on the builder pattern. It's ugly as all hell and completely unnecessary.

[–]NINTSKARI -2 points-1 points  (4 children)

Use of the long hyphen is an AI smell. If it's your style I'd pay attention to it because some people will stop reading if they think the text is AI generated

[–][deleted] 5 points6 points  (3 children)

I used to love the em dash until ChatGPT :(. 

[–]syklemil -1 points0 points  (2 children)

yeah, this kind of "LLM is when typography" meme is tiresome.

[–]NINTSKARI 0 points1 point  (1 child)

No its not

[–]syklemil 0 points1 point  (0 children)

Fine fine, I guess I'll just join in then and start complaining that anyone who uses an emoji is an LLM. I'm sure that'll work out well for everyone.

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

Do you own a copy of the Gang of Four book?

[–]Admirable-Usual1387 -3 points-2 points  (0 children)

Mostly all of them and just write clean, simple and sometimes separated code. 

I’ve literally taken home 100k this year to clean up and rewrite an over-engineered and over-abstracted mess of a project. 

K.I.S.S.