One of Python's greatest strengths is its tendancy to reduce many of
the common mistakes you do in other languages. But the language is not
perfect, and it introduces its own bug sources. For example, try
finding the bug in the following snippet:
class Vector:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
class Ray:
def __init__(self, direction, origin = Vector(0, 0, 0)):
self.direction = direction
self.origin = origin
ray1 = Ray(Vector(0, 0, -1))
ray2 = Ray(Vector(0, 0, 1))
ray3 = Ray(Vector(-1, 0, 0), Vector(2, 3, 4))
Does it look correct to you too?
It kind of does to me, but on closer inspection one finds that there
is an "obvious" bug in there. The line:
def __init__(self, direction, origin = Vector(0, 0, 0)):
is wrong. Default arguments, such as the origin parameter is evaluated
only when the module is loaded. Therefore, all Ray objects, whose
origin Vector is created from the default argument, will share the
same Vector instance.
>>> r1 = Ray(Vector(1, 2, 3))
>>> r2 = Ray(Vector(3, 2, 1))
>>> r1.origin.x = 33
>>> print r2.origin.x
33
That is certainly not what the programmer desired. Ray's __init__ must
be fixed so that a new origin Vector is created every time the
constructor is called with a default argument:
class Ray:
def __init__(self, direction, origin = None):
if origin is None:
origin = Vector(0, 0, 0)
self.origin = origin
self.direction = direction
This is a fair bit uglier than the previous example. For me, these
types of bugs are caused because I really, really do not like the
if
blaha
is
None:
idiom. But I know it is time for me to just accept
that you must
never
use mutable default arguments.
The behaviour is one of Python's warts which bites all newcomes to the
language. Coming from languages where the norm is that function
definitions are compiled, it is hard to grasp that Python actually
executes them. The gotcha is documented in numerous sources:
So someone got the pretty logical idea that changing how default
arguments is evaluated should be changed. It would both help newbies,
reduce many lines of code and help developers write more correct code.
The
idea
was brought up on the Python-3000 mailing list. But was
quickly
rejected
by the BFDL since it was a too big change and breaks
orthogonality with class variables. And that was that. Well, there was
also a few who even
defended
the current behaviour and thought that
changing it would not
help
anyone. It must have been a long time
since these people read introductionary material to Python and
encountered the "do not use mutable default argument values!"
warning. :)
But the proposed semantical change definitely was to big and it would
break orthogonality with other language features. I just hope that
someone else will invent a new and better way to solve the
problem. Python's incapability of handling mutable default values
nicely is a big wart that someone hopefully can find a solution for.