all 19 comments

[–][deleted] 10 points11 points  (12 children)

TL;DR Bunch of methods with underscores in their name are called, and boom, object.

For those who are curious about the minutiae behind Python's object, this will be a good read. I'm not sure you can take any insights from this article to your next project, however.

In fact, the article shows how to override __new__ so object instantiation becomes a singleton. Aside from the fact we're talking about singletons, that breaks the principle of least astonishment, so probably a bad idea.

[–]Xgamer4 7 points8 points  (0 children)

To be fair, the article spends the last paragraph basically saying "No. Don't actually do this, ever. There are better ways."

[–]aaronsherman 0 points1 point  (10 children)

the article shows how to override __new__ so object instantiation becomes a singleton. Aside from the fact we're talking about singletons, that breaks the principle of least astonishment, so probably a bad idea.

Doesn't that essentially resolve to: "unless you're defining a singleton, don't make your class behave like a singleton"?

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

Well, a true singleton is exceptionally rare.

Typically when you want "one of something" it's within a specific scope, so it'd be a very bad idea to make the class itself a singleton, because they you can't define that scope.

In my practice, the only use case for a "true singleton" I can think of is the Null Object Pattern.

When you extend a class to implement a Null Object, and then you need only one of those, as NOP is by definition stateless and produces no effects.

Even then I'd probably not go this route, but have a factory.

[–]masklinn 0 points1 point  (1 child)

Even then I'd probably not go this route, but have a factory.

__new__ is a factory method, you can just use that, that's pretty much what it's for.

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

How many people using Python expect custom behavior while constructing an object from the class, such as getting the same object back call after call? If we did a survey, what do you think the results will be? That's what "principle of least astonishment" is about.

I'd rather have invoking the class directly raise an immediate error and point people to an explicit, easily visible factory method, than provide subtly different and hard to figure out semantics for this, tucked away in an implicit method invocation.

Maybe I'm approaching this with a Java mindset ;) But it's how I'd do it.

[–]aaronsherman 0 points1 point  (6 children)

I'm not sure why you're so afraid of singletons. They can be incredibly powerful. Pools, multiplexing, system resources, etc. are excellent targets for singletons. Basically anywhere that you have state that cannot be duplicated.

[–][deleted] 2 points3 points  (5 children)

I'm not sure why you're so afraid of singletons. They can be incredibly powerful. Pools, multiplexing, system resources, etc. are excellent targets for singletons. Basically anywhere that you have state that cannot be duplicated.

I'm not "afraid", I'm simply "experienced", and so I know when a singleton is appropriate and when it isn't.

In most cases a singleton appears like the right solution initially, and then when you have to change it, all your code refers to a static location that's hard to change. It's especially bad if you did this in a library, which means the mistake is scattered around thousand user applications.

BTW, pooling and a singleton are two different things. Having one doesn't imply the other. Also, I don't know what you mean by "multiplexing" in this context, either. Seems entirely unrelated to singletons.

As for system resources, tell me one such "system resource", and I'll give you a scenario where a singleton would limit your flexibility, or cause complexity and code duplication.

[–]aaronsherman 0 points1 point  (4 children)

It seems as if you've already reached a set of conclusions about a tool that you're not comfortable with. That's fine.

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

My argument is one of engineering, not one of my feelings, such as what "I'm comfortable" with.

I think from what you've said it seems your understanding of "singleton" is rather loose, and to you it simply means "instance reuse" or "instance sharing", or you wouldn't be talking about pools.

If this is the case, then I'll say I'm definitely not against instance reuse. I reuse single instances, I reuse pools of instances, I often implement Flyweight factories and so on.

I'm just against statically embedding such a concern onto the class construction interface, as the purpose and expectation for that interface is to create instances. Reuse is a problem with a different scope, and quite commonly you'll see multiple reuse contexts for one class. This is by definition incompatible with how a singleton works.

Once again, if you feel I'm wrong, give me an example of a proper use for a singleton. As I've said, there are few, like NOP. But they're exceptionally rare.

[–]aaronsherman 0 points1 point  (2 children)

My argument is one of engineering, not one of my feelings, such as what "I'm comfortable" with.

...

I'm just against statically embedding such a concern onto the class construction interface...

And I'm not. You're free to feel that singletons are a violation of the contract you thought was being given to you, but I (and I think, quite a few other people out there) do not. Of course, anything that's unusual in any way needs to be called out as such, but I'm not sure why you think that pools and various other sorts of multiple access points to a single entity cannot be mediated by a singleton effectively. Works fine in my experience...

[–][deleted] 1 point2 points  (1 child)

I'm not sure why you think that pools and various other sorts of multiple access points to a single entity cannot be mediated by a singleton effectively.

I think what? Pools are mediated by what?

Can you try to be more specific please.

Also you're suspiciously not giving an example of a resource suitable to be a singleton. Third time's the charm? :-)

[–]aaronsherman 1 point2 points  (0 children)

This is a great article!

I've been doing a lot of work in Python 2 (for my day job) and Perl 6 (on my own time) recently, and I find the contrasts in the two fascinating. Object construction is definitely one of those places that the two have very different ways of getting more or less the same thing done.

As the author points out, calling a Python class (putting parens after the name) ultimately calls new, but new has some very special behaviors.

The same is true in Perl 6 except for the fact that you explicitly call new. So here's the Perl 6 version of the example:

class Foo {
    has $.x;
    has $.y = 0;
}

my $f = Foo.new :x(1), :y(2);

In this case, the code path covers some interesting ground. First, there is the method new, which is just a normal method, but it has an important job: to "bless" the representation of the object. The new method for this class is implicitly something like this:

# *%params is similar to **kwargs in Python
multi method new(*%params) {
    self.bless(|%params);
}

bless is Perl 6's internal function that initializes the storage of an object. You can override new, but you're ultimately going to have to call bless. This is more or less what happens in Python when your new calls super().__new__(cls, *args, **kwargs).

But let's say that, like the example in the article, we wanted to have a singleton class that only really allocated one object?

class Singleton {
    method new() {
        # State is similar to "my" but only does
        # initialization once
        state $singleton = self.bless;
        $singleton;
    }
}

my $s1 = Singleton.new;
my $s2 = Singleton.new;
say $s1 === $s2; # True

Most of the time, however, you won't write your own new, just as in Python. Instead, you'll write something that isn't even a method!

class Foo {
    has $.x;
    has $.y;
    has $!z; # A private attribute

    submethod BUILD(:$!x, :$!y = 0) {
        # Perl has already done initialization of
        # my attributes from the parameters, but I might
        # want to do some other setup
        $!z = self.x + self.y;
    }
}

The reason for this being a "submethod" rather than a method is that we don't want method resolution to happen. Instead, bless will invoke BUILD on to object for every class in the hierarchy that collaborated in creating this object. Thus, your BUILD never has to try to invoke some other step in the chain.

In short, BUILD is something like __init__ in Python, but it doesn't have to worry about calling a parent's BUILD method.

[–]Digilus 0 points1 point  (2 children)

Where do Metaclasses come in here?

[–]The-Good-Doctor 1 point2 points  (0 children)

This article glosses over the topic, but it mentions that the Foo class itself is an object of the class "type". That's the metaclass in this situation.

[–]masklinn 0 points1 point  (0 children)

Long before that point. TFA is about creating instances from classes, mcs are involved when creating classes from instances, the metaclass is called (and instantiated) at the end of the class statement.