This is the eighth in a series of blog posts I’ve been working on during COVID isolation. It started with the idea of refreshing my systems design and software engineering skills, and grew in the making of it.
Part 1 describes ‘the problem’. A mathematics game designed to help children understand factors and limits which represents the board game Ludo.
Part 2 takes you through the process I followed to break this problem down to understand the constraints and rules that players follow as a precursor to implementation design.
Part 3 shows how the building blocks for the design of the classes and methods are laid out in preparation for understanding each of the functions and attributes needed to encapsulate the game.
In Part 4 we get down to business… producing the game using the programming language PHP with an object orientated approach.
Part 5 stepped it up a notch and walked through a translation of the code to object orientated C++.
Part 6 delves into the world of translating the code into object-orientated Java.
Now in Part 8, after taking a long, deep breath, I slide into the sinuous world of Python.
Get in touch on Twitter at @mr_al if you’d like to chat.
Run-time available at: http://alistairlloyd.com/ootest/py/
I took a long run up at Python and was prepared to hate it. OK, hate is a strong word, so hear me out. I’d taken a look at it a few years ago and was underwhelmed. Compared to some of the more mature languages I’d dealt with so far, and the hype surrounding what Python was allegedly capable of doing, I was struggling to understand where it sat in the pecking-order of power and respectability.
That said, I was prepared to set aside prejudice and give it a go. From the outset there were a few things that I struggled with. In no particular order, these were (are):
1. A dearth of brackets – All of the languages I’d used for the Auto120 algorithm so far relied on a variety of parentheses to contain methods, loops and conditionals. In an ironic twist for a language named after a writhing, curling snake, there was none of that to be had. Instead, Python relied on – shudder – spacing.
Do you remember the episode of Silicon Valley where Richard’s girlfriend prefers to use Spaces over Tabs? This sort of thing is a holy war that will never be resolved. Such is Python’s understanding of exactly where you intend to terminate a class, loop, method or conditional. A relaxed attitude towards this led me to other issues, but more on that later.
2. Terminate how? – It is amazing how quickly you get used to ending lines of code in a semi-colon. Not with Python, which considers the end of the line… the end of the line. A new-line character does exactly what it says on the pack – tells the compiler that the current line of code has ended, and that the next line of code is separate. This felt like driving without a seat belt on. You could do it, but it felt at best oddly uncomfortable and at worst dangerously unsafe.
3. Explicit self-reference – When declaring a Method within a Class, it took me a while to figure out that the Method did not auto-magically pass a copy of ‘itself’ when executing, meaning that they needed to be declared as such:
This seemed a bit avoidant, but after a while I got into a rhythm transcribing and was prepared to let it go. It took some getting used to the fact that calls to the Methods from *outside* the Class did not require the ‘self’ call, which resulted in the awkward practice of having a different number of arguments passed to the call than was defined in the declaration.
4. Pass the parcel – One part of Python that I became endeared to – in much the same way as you admire the arty/rebel kid who you think is cool mainly because you don’t have to parent them because they belong to someone else – is the ‘pass’ command. Effectively, this does nothing, but it does it with style.
What I like about ‘pass’ is that it fills the space that would otherwise be empty, and in doing so, allows for a degree of comfort and aesthetic in coding that would otherwise be blank space. Confused yet? Don’t be.
Why is this important? For the reasons I mentioned above…Python’s lack of terminating punctuation (in the form of semi-colons or parentheses) means that the non-executed ends of conditionals or other logical branches would otherwise dangle like a grammatical participle. ‘pass’ could thus be used to help the code-reader (or engineer) to show the logical conclusion of a condition, such as:
5. Matryoshka arrays – Declaring and instantiating an array of Classes was a recurring theme for me through my code-porting exercise. Especially, overcoming the different languages’ grammar for how to both create and populate an array element at the same time proved a head-banging exercise that left a neat row of mirror-reversed QWERTY marks on my brow.
Python liked things explained to it in simple terms. As I get more used to this language I’m sure my approaches will improve, but sometimes in coding we all do things to get by and to let us get on to the “next thing”.
In this case, it was how to create the array of Markers (the object p_pieces) in the constructor method of Player. (known in Python as “__init__” as calling it a “constructor” would just make life too easy.)
So, I cheated and did this:
(where x_id is the number of the Player, either 1 or 2).
This simple and inelegant bit of code did what I needed – it created an array object called p_pieces consisting of five Markers which themselves were instantiated by being brought into existence by this call. Done. On to the next thing.
6. Returning what, exactly? – Like php, Python did not expect a Class method to be declared with the type of object that it was returning. This was useful but caused other issues which I tripped over in Ruby, later.
7. Roll the dice, pick a date – With a brow-mopping sense of relief, Python’s random integer number generation is a breeze compared to some of its forebears. Inclusion of the ‘random’ library allows for the following:
…where, funnily enough, you get an integer random returned between the values of 1 and 6, inclusive. Too easy.
Date parsing was slightly more convoluted, but no more than any other languages. Following an import of ‘datetime’, one could do:
Where g_now is a string and holds the date/time stamp, and the strfttime (string/format/time) method converts it to read something like 2019-11-21 14:45:22. (used, as above, to feed a value into the g_log_move method).
8. Finding value in an array – Thank the coding gods that this was easier than other languages. Returning a Boolean by looking for value x_temp_value in an array named x_temp_magic_numbers was as easy as:
9. Loops within Loops – This did my head in. Once the code was complete and would execute, I found whole swathes of methods not being executed or called, leading to infinite runs of looping games that didn’t move beyond a logical point.
I had wondered if I had fallen victim to my usual coding trick – poor punctuation terminating a section of code – but given that Python has a low dependency on such things, it fell to me trawling through the code and insert lots of debug markers to trace exactly where the code was going.
This took a long while to track down, and in doing so identified two different problems.
Problem number 1: The logic that searches for penultimate and factor targets (g_target_magic_numbers) was not executing correctly, resulting it returning values that were leading to ‘blowouts’ of Player Markers. This meant that games were going far longer than normal. Additionally, the method that sought out opposition Player Markers to target was not behaving, often seemingly finding, then ignoring, juicy targets. So, what to do?
Lessons from the French
In a typical IDE for parentheses-bounded code, helpful markers will provide a visual indication of the start and end of an iteration, method or conditional. However, with Python being based on spacing, the indications don’t exactly come out and slap you in the face.
Visual Studio has tiny tick-marks to indicate blocks of code. Visual Studio Code has long vertical lines. Visual Studio lets you ‘collapse’ blocks of code using the [-] tool… but for conditionals will only collapses them up to the point of an ‘else’ statement. Visual Studio Code has down-arrows which only appear when you hover over the far-left of the IDE next to the line numbers… and again, it only collapses code to the point of an ‘else’.
But you know what the French apparently said in the 13th century – “Mauveés ovriers ne trovera ja bon hostill!” or, more colloquially – “A bad workman will blame his tools”.
Spacing and tabs issues had become my new punctuation issues. All throughout the Games class I had been sloppy with spacing, often with a single character of alignment bringing the difference between a neatly blocked set of code, and a logical behemoth which kept going until it fell through a catch all at the end. The issue was compounded across various paragraphs and methods, forcing me to go through line by line, with comment markers acting as a proxy to help me visualise where things ‘should’ end versus where they ‘were’ ending.
OK – I got that sorted and started to see valid outputs and runs.
Wrapping up on Python
I can see why people like Python, I really do. For me it involved a lot of ‘unlearning’ and keeping an open mind to what a more contemporary approach to languages meant. I also enjoyed the power of what Python allowed me to do, particularly when it came to shelling out to a command line and being able to run multiple instances (in this case, through Windows Powershell) and pipe the results accordingly.
It was also very transportable; with a few tweaks to the calling class I was able to run the same code up on my cPanel/Linux service with ease.
Discipline in coding is a good thing (says he, like a mantra) and we all know the pain of picking up someone else’s code and spending hours figuring out ‘what on earth have they done, here…?’. That said, in my experience one of the best ways of learning about any sort of technology is picking something up that is ‘broken’ and figuring out how it works. I mean – hey – it was already broken, what’s the worst you can do. Right?
NEXT POST… It’s a darling of song titles, a cardinal gem and the name of our family dog. In the next post I take a multi-faceted approach to Ruby. (See what I did there? hashtag dadjoke)