Python Annoyances
Python’s goals include simplicity, lack of unnecessary boilerplate and being easy to understand.
Python is really nice, I like it. But it makes coding so easy, even the tiniest rough edges start feeling annoying.
When you renovate just your kitchen, the rest of the house starts looking really bad.
© 2008-2009 Tero Karvinen www.iki.fi/karvinen
Type Casting Required
>>> 1/2 0
Not what you expected? Of course, if you program washing machines in C, this is excatly what you expected.
One is an integer, two is an integer, 1/2 is an integer divide and there is not even a warning that the result is wrong.
Of course, you should have written it just like in C or C++:
>>> float(1)/float(2) 0.5
or less explicitly use floating point numbers to start with:
>>> 1.0/2.0 0.5
The problem is, if wanted to write in C, I would – write in C. Also, Python seems to have hard time decinding if it has typing or not.
You could also look at the problem from the point of what a programmer would want. I can’t imagine that someone would want 1/2 to be 0, ever. So the best fix would be automatic typecasting.
Future of Division
Future will bring a fix to integer division annoyance. Notice the four underscores around “__future__”.
$ python >>> from __future__ import division >>> 1/2 0.5
If you are in a hurry, you can enable the future when invoking python shell.
$ python -Q new >>> 1/2 0.5
If you use interactive python shell as your calculator, you can even make this change permanent.
# ~/.pythonstartup - default settings for interactive python console # Does not affect scripts. # Enable from your startup scripts, such as ~/.bashrc # $ export PYTHONSTARTUP=$HOME/.pythonstartup # (c) 2009 Tero Karvinen http://www.iki.fi/karvinen from __future__ import division # so that 1/2==0.5 and not 0.
Are You Reserved?
Consider
#!/usr/bin/env python import logging logging.info("Tero is testing logging module...")
But when run
$ ./logging.py Traceback (most recent call last): File "./logging.py", line 2, in <module> import logging File "/home/tero/Code/pyAnnoy/logging.py", line 3, in <module> logging.info("Tero is testing logging module...")
Logging is a standard module in Python. It might even be included in every Python installation, according to Pythons “batteries included” aproach.
It’s obvious that I made a mistake calling my script with a reserved name. Maybe Python intrepreter could have told me just that.
When you’re testing a module, the first name that pops up in your mind is quite likely to be the same as one of the modules you’re testing. Especially because Python modules have very generic words as names.
Fix would be simple: using a reserved name would produce an error: “Error: “logging.py”: “logging” is reserved for built in module “logging”. Rename your file. “.
Seekers of Truth
NameError: name 'true' is not defined
For booleans, the error messages could mention they are spelled with a Capital First Letter, “True” and “False” instead of “true” and “false”.
Is there a specific meaning in the capital first letter? Is “True” a name of a class?
Me, mySelf, and I
This class definition has a minor error
#!/usr/bin/env python # This code has an error! class Carrot: bitesLeft=5 def takeBite(): self.bitesLeft=self.bitesLeft-1 # main terosCarrot=Carrot() print "Tero's Carrot has", terosCarrot.bitesLeft, "bites left." print "Biting" terosCarrot.takeBite() print "Now Tero's Carrot has", terosCarrot.bitesLeft, "bites left."
When run
$ ./self.py Tero's Carrot has 5 bites left. Biting Traceback (most recent call last): File "./self.py", line 12, in <module> terosCarrot.takeBite() TypeError: takeBite() takes no arguments (1 given)
Of course, methods should always have “self” as the first argument
def takeBite(self):
After this fix, it runs as expected
$ ./self.py Tero's Carrot has 5 bites left. Biting Now Tero's Carrot has 4 bites left.
If methods should always have “self” as the first argument, couldn’t Python just figure that out internally? In the beginning, it was really annoying to try to figure out when to add self and when not.
Python intrepreter seems quite egoistic when it always wants to talk about self. What purpose this boilerplate “self” serves?
Exit Only
NameError: global name 'EXIT_FAILURE' is not defined
When a program exits, you have to tell if it went OK or if the program failed. We all know you are not supposed to use magic numbers in code (like “sys.exit(237)” or “sys.exit(1)”). So, we name our constants. But why EXIT_SUCCESS=0 and EXIT_FAILURE=1 (or EXIT_FAILURE=2) are not defined automatically? Why aren’t these batteries included?
Giving exit status codes another look, I found
>>> import os >>> print os.EX_OK 0
Still, I couldn’t find a generic EXIT_FAILURE code defined. There are some specific ones in the os “Miscellaneous operating system interfaces” module.