I'm quite old to know that there are other languages more suitable for scripting, but I was asked to use more fancy (and less efficient) programming languages for scripting, because the new generation of developers doesn't even know about Perl and Shell scripting languages. I have to use Python to earn money and I'm not happy with this.
Introduction
Python is not a brand new programming language. It has got quite a long history of development. But it has still has got some unexplainable issues and I will try to reveal them within the article below.
Background
I was asked to use Python instead of Perl recently. I understand that Perl is still strong enough, but its development is a bit frozen. And it is strategically wise to switch to other languages with more active development to avoid dead end way. So, I don't really hate the modern programming languages, but I have to notice some issues that are making support process of the software created with these languages more difficult and exciting.
Using the Code
A code in the article contains the examples of the things that you must not do. Please don't use it at work and, of course, at home. Please avoid these example code patterns in the working code. The code has been written and "tested" with Python 3.11.
What Can You Catch From Python Code
Obvious Coding Issues
Here is an example of the text processing Python class:
import sys
import platform
from io import TextIOWrapper
class FileReader:
def __init__(self, inFileName: str, outFileName: str):
plName = platform.system()
if plName == 'Windows':
self.isWindows = True
self.newline = "\r\n"
else:
self.isWindows = False
self.newline = "\n"
if len(inFileName) < 1:
self.stdin = TextIOWrapper(sys.stdin.buffer,
newline="",
encoding="utf-8")
else:
self.stdin = open(inFileName, "r", 1, encoding="utf-8")
if len(outFileName) < 1:
self.stdout = TextIOWrapper(sys.stdout.buffer,
newline=self.newline,
encoding="utf-8")
else:
self.stdout = open(outFileName, "w", 1, encoding="utf-8")
self.someData = ''
def getData(self) -> str:
return self.someData
def decodeSomeThing(self, txt: str, al1: list) -> str:
atx = txt.split(',')
al1[1] = '3'
txt = atx[0]
return txt
fr = FileReader('','')
as1 = ['1','2']
s1 = 'some, things'
s2 = fr.decodeSomeThing(s1,as1)
print(s1,s2, as1)
print(fr.getData)
Please take a look at the last row of the code example above. I expected an error output like:
AttributeError: 'FileReader' object has no attribute 'getData'
But there is not:
some, things some ['1', '3']
<bound method FileReader.getData of <__main__.FileReader object at 0x7f950a5befd0>>
It is connected to the other language features, but it makes development and support process more exciting. :-)
Most of the things in Python will be passed by reference, but some are not. And you must learn it to make your code more predictable. Take a look at the rows with comments. Value of the s1
remains unchanged outside the function decodeSomeThing()
, but the value of the as1
will be modified (see terminal example above).
Integer rounding "issue"
I was not expecting to see the integer rounding error issue below, but I saw it:
import sys
class MultiNumber:
CHARSET64 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz, '
CHARSET36 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
def __init__(self, charz: str = CHARSET64):
self.charz = charz
self.ord = len(self.charz)
def encode(self, num: int) -> str:
s1 = ''
n1 = num
while n1 > 0:
n2 = int(n1 % self.ord)
s1 = self.charz[n2] + s1
n1 = int(n1 / self.ord)
return s1
def decode(self, snum: str) -> int:
mul = 1
n1 = 0
i = len(snum) - 1
while i >= 0:
s1 = snum[i]
ni = self.charz.find(s1)
if ni > 0:
n1 = n1 + (ni * mul)
else:
if ni < 0:
sys.stderr.write("Unable to decode character:'{0}' at pos {1}".format(s1, i))
break
mul = mul * self.ord
i = i - 1
return n1
def main(argv):
mn = MultiNumber(MultiNumber.CHARSET36)
for arg in argv:
n = int(arg)
s1 = mn.encode(n)
n1 = mn.decode(s1)
print('{0:X} -> "{1}" -> {2}'.format(n, s1, n1))
if __name__ == "__main__":
main(sys.argv[1:])
When I run the code above, I will receive the output:
-bash $ ./multinumber.py 9223372036854775807
7FFFFFFFFFFFFFFF -> "1Y2P0IJ32E807" -> 9223372036854775303
The encoding result is not valid (proper result must be "1Y2P0IJ32E8E7
"). And when I run the C implementation of the encoding:
char* encode(int64_t n1, const char* charz)
{
int64_t n2;
static char s1[12];
int64_t ord = strlen(charz);
char* ptr = (s1 + sizeof(s1) - 1);
char chr;
*ptr = '\0';
while (n1 > 0) {
n2 = n1 % ord;
chr = charz[(size_t)n2];
ptr --;
*ptr = chr;
n1 /= ord;
}
return ptr;
}
...
char* s1 = encode(9223372036854775807, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
I receive a proper result.
Integer rounding "issue" explanation
As it was highlighted by Matthew Barnett in comments, my mistake was in syntax. That's not a rounding error as I thought. As from my previous experience with the other languages, mathematical operations are performed after all the operands are converted to the same type. And I was sure that Python does an implicit conversion to the result type for all the operands. But for integer division there is another operator (//
) that I haven't noticed about. Well, I need to learn this lesson and my study for Python is far away from its end. Nobody is perfect, even if someone thinks so.
Points of Interest
I did not see anything exceptional with the issues highlighted in the article above. They are usual consequence of the programming languages development. I hope they will be fixed in the next releases. Or such a things have just highlighted more prominent in the documentation.
History
- 25th October, 2023: First edition
- 25th October, 2023: Second edition (some issue fixed)
- 27th October, 2023: Logical mistake highlighted and explained (I hope so)