does python have useless destructors?

David Turner dkturner at telkomsa.net
Tue Jun 15 13:11:46 CEST 2004


Peter Hansen <peter at engcorp.com> wrote in message news:<auqdneR-tKpSXlDdRVn-uQ at powergate.ca>...
> David Turner wrote:
> 
> > I *want* to spend more time in Python, but as things stand my C++ code
> > is more robust.
> 
> Is this a theoretical definition of "robust", or a practical one?
> In other words, do you mean you Python code actually fails more
> often than your C++ code, or do you just mean that, theoretically,
> the uncertainty with Python destructors makes you feel that your
> Python code is less reliable?

I mean that it's very much easier for me to write provably correct
code in C++.  Trying to do the same in Python is an uphill struggle. 
Furthermore, the C++ version is far more maintainable; the coding
style is *intentional*.  For big systems with multiple programmers
using each other's objects, there's no question that I'd use C++.

Of course, for small projects, Python is miles ahead of C++.  I'd like
to see that advantage extended to large projects as well.  I hope that
the complications involved in writing C++ code are accidental rather
than inherent qualities of a big-system language.


> In my experience, and I know also the experience of *many* others,
> our Python programs are very much *more* reliable than our C++
> programs.  In fact, I've rarely if ever had a Python program that
> failed in any way related to Python's destructor behaviour.  On
> the other hand, I've rarely if ever had a C++ program which did
> not crash my entire system at least once during development...

"...in any way related to Python's destructor behaviour" is the key
part here.  Python programs don't fail so much as misbehave.  If they
don't misbehave (and they're doing anything complicated), the code is
often nested several layers deep in try/finally blocks.  It's ugly,
and hard to maintain.

In my experience, the main reason for this is that the onus is on the
user of the API to use it correctly.  The most trivial examples of
this are acquire/release cycles in things like mutexes.  There are too
many ways in which a user can fail to release a mutex, either through
forgetfulness, or through not considering all possible paths of
execution (which is difficult to do!).  Now, interface contracts can
remind the user when he needs to do something, and help to trace a
fault, but they don't necessarily make the user's job any easier (or
quicker).  The C++ community, on the other hand, is working towards
ways of *enforcing* API contracts, by reworking the API in such a way
that it can't be abused.  Again, the mutex is a handy example of this.
 Consider the following C++ code:

class mutex {
private:
    void acquire();
    void release();
public:
    class lock {
        mutex& m_;
    public:
        lock(mutex& m): m_(m) { m_.acquire(); }
        ~lock() { m_.release(); }
    };
};

The acquire() and release() methods are private, so the only way to
get at them is through the lock class.  Ownership of the mutex is thus
represented by the lifetime of the lock object.  This makes it very
difficult for the user of the mutex to abuse the acquire/release
cycle.

Critics will point out that a devious user could in fact circumvent
these guarantees by allocating the lock on the heap.  If that be the
case:

class mutex {
private:
    void acquire();
    void release();
    class lock_ { /* as before */ };
public:
    typedef std::auto_ptr<lock_> lock;
    lock get_lock() { return lock(new lock_(*this)); }
};

Were I to meet anybody who could circumvent such a design after its
declaration, I'd probably hire them - after making them promise never
to do it again.

Regards
David Turner



More information about the Python-list mailing list