[Tutor] Long post: Request comments on starting code and test code on chess rating project.

Steven D'Aprano steve at pearwood.info
Sun Aug 13 02:42:04 EDT 2017


I haven't had a chance to read the entire post in detail, but one thing 
which stands out:

On Sun, Aug 13, 2017 at 12:22:52AM -0500, boB Stepp wrote:

> I have started my coding with a RatingCalculator class.  The intent of
> this class is to gather all methods together needed to validate and
> calculate chess ratings.  My intent is to only ever have a single
> instance of this class existing during a given running of the program.
> (BTW, is there a technique to _guarantee_ that this is always true?)

Yes, this is called the "Singleton" design pattern, which is very 
possibly the most popular and common design pattern in OOP.

It is also very possibly the *least appropriate* use of OOP techniques 
imaginable, so much so that many people consider it to be an 
anti-pattern to be avoided:

https://www.michaelsafyan.com/tech/design/patterns/singleton

(follow the links at the bottom of the page for more information about 
why singletons are considered harmful).

It is popular because:

- it is easy to understand;

- it is easy to program, at least in languages like Java and C++ 
  (less so in Python);

- consequently it is often the first OOP design patterns taught;

- and it makes up for a deficiency in Java, or at least the Java
  philosophy, namely the lack or avoidance of top-level functions.

http://steve-yegge.blogspot.com.au/2006/03/execution-in-kingdom-of-nouns.html

In Python, the equivalent to the singleton is the module. If you have a 
class full of methods which is only ever going to be instantiated once, 
*almost always* the solution in Python is to use a module full of 
functions.

There are few exceptions, but they're generally identity-objects only, 
with little or no state and few if any methods:

- None
- NotImplemented
- Ellipsis
- True and False (okay, duotons not singletons)

I can't think of any other obvious examples.

By "identity-object", I mean an object whose value is synonymous with 
its identity, rather than two separate aspects of the object.

With regular objects, their value is independent of their identity. 
Their identity is "which particular object is this?" while their value 
is separate: you can easily have you have two floats with same value 
(say, both 1.25) without them necessarily being the same object. 
Likewise we can have two distinct lists which happen to have the same 
value: they both have the value [1, 2, 3], but they're different lists 
with the same value rather than the same list.

With regular objects, just because they have the same value doesn't 
necessarily mean they must be the same object, they may be distinct 
objects with the same value.

But the value of None is not separate from its identity. None has no 
value *except the fact that it is the None object*. Its value is not 
separate from its identity, its value is its identity.

So, coming back to your RatingCalculator class:

(1) Can you think of any possible circumstances where you might want two 
distinct RatingCalculators at the same time? Could you not have two 
different calculation methods? If so, then use a class, but allow it to 
be instantiated as often as you need.

(2) If there is absolutely no possible reason to want two such 
calculators, then there is little reason to use a class at all. 
Singletons are just global-level state disguised as an object.


A third option:

Look at the source code for the random module. It has a Random class, so 
that you can have as many different, *independent* random number 
generators as you need. They can be subclassed to use different 
mechanisms for generating pseudo-random numbers, or they can use the 
same algorithm but be in different internal states.

But since *most* people don't need more than one RNG at a time, there's 
also a hidden instance, pre-instantiated when the module is imported for 
the first time, which is used to provide module-level RNG functions. The 
caller doesn't need to know that under the hood there's a private 
instance. Advanced users can create their own instances, while simple 
cases just call the module-level functions.



-- 
Steve


More information about the Tutor mailing list