Python style guidelines

MetalOne jcb at iteris.com
Sat Mar 13 16:28:26 EST 2004


> It's that "resources acquired" part that messes people up.  I've
> seen it over and over and over again.  Memory leaks.  Dangling
> pointers.  Unrecoverable file handles.  Especially as functions
> that acquire lots of resources evolve over time and someone adds
> code to acquire another resource or detect another error condition
> and then doesn't find all the subsequent exit points.  It's less
> of an issue with languages with proper garbage collection, but it
> can be a real nightmare in (e.g.) C or assembler.  Unit tests that
> cover all possible resource leaks are difficult to construct
> correctly.  C++ partially addresses the problem with RIAA (if
> coders use it properly) and a hodgepodge of "smart" objects that
> do pseudo self-garbage collection.
> 
> A single exit point also gives me a fighting chance to add
> something to the logical end of a function, like setting the "I'm
> done" flag or logging the result.
> 
> If I know that my function only has one exit point, then it's also
> easier to add "let's see what this function is returning"
> debugging code, because I know exactly where to put it and where
> to look for it later when I want to take it out.  In some systems,
> the fact that a given function reaches its (single) exit point is
> extremely valuable information.
> 
> OTOH, I'm usually the first one to stand up at a design review and
> declare that some function or some method is too long or too
> complex and should be broken up *before* such problems occur.
> 
> And that all said, I agree that a bunch of pre-checks and a quick
> exit, BEFORE DOING ANYTHING ELSE, near the top of a function can
> make the rest of some functions much more clear.  Again, I've seen
> it a million times:  I'll add one more validation down here, but
> forget to release that memory we just acquired up there.
> 
Good points.
With resource acquistion, I was mainly thinking along the lines of

int file_compare(const char *filename1, const char *filename2)
{
    FILE *infile1 = fopen(filename1, "r");
    FILE *infile2 = fopen(filename2, "r");
    if (!infile1 || !infile2)
    {
        // error
        if (infile2)
            fclose(infile2);
        if (infile1)
            fclose(infile1);
        return FAILURE;
    }
    
    // compare files
    return SUCCESS
}

as opposed to

int file_compare(const char *filename1, const char *filename2)
{
    int result = SUCCESS;
    FILE *infile1 = fopen(filename1, "r");
    if (infile1)
    {
        FILE *infile2 = fopen(filename2, "r");
        if (infile2)
        {   
            // compare files
        }
        else
        {
            result = FAILURE;
        }
    }
    else
    {
        result = FAILURE;
    }
    if (infile2)
        fclose(infile2);
    if (infile1)
        fclose(infile1);
    return result;
}



More information about the Python-list mailing list