is there any principle when writing python function

Steven D'Aprano steve+comp.lang.python at pearwood.info
Fri Aug 26 20:16:30 CEST 2011


Tobiah wrote:

> 
>> Furthermore: If you are moving code out of one function to ONLY be
>> called by that ONE function then you are a bad programmer and should
>> have your editor taken away for six months. You should ONLY create
>> more func/methods if those func/methods will be called from two or
>> more places in the code. The very essence of func/meths is the fact
>> that they are reusable.
> 
> While I understand and agree with that basic tenet, I think
> that the capitalized 'ONLY' is too strong.  I do split out
> code into function for readability, even when the function
> will only be called from the place from which I split it out.

In other words, you disagree. Which is good, because the text you quote is
terrible advice, and it is ironic that the person you quote judges others
as bad programmers when his advice is so bad.

I can think of at least five reasons apart from re-use why it might be
appropriate to pull out code into its own function or method even if it is
used in one place only:

(1) Extensibility. Just earlier today I turned one method into three:

    def select(self):
        response = input(self)
        if response:
            index = self.find(response)
        else:
            index = self.default
        return self.menuitems[index-1]

turned into:

    def choose(self, response):
        if response:
            index = self.find(response)
        else:
            index = self.default
        return self.menuitems[index-1]

    def raw_select(self):
        return input(self)

    def select(self):
        return self.choose(self.raw_select())


I did this so that subclasses could override the behaviour of each component
individually, even though the caller is not expected to call raw_select or
choose directly. (I may even consider making them private.)

(2) Testing. It is very difficult to reach into the middle of a function and
test part of it. It is very difficult to get full test coverage of big
monolithic blocks of code: to ensure you test each path through a big
function, the number of test cases rises exponentially. By splitting it
into functions, you can test each part in isolation, which requires much
less work.

(3) Fault isolation. If you have a 100 line function that fails on line 73,
that failure may have been introduced way back in line 16. By splitting the
function up into smaller functions, you can more easily isolate where the
failure comes from, by checking for violated pre- and post-conditions.

(4) Maintainability. It's just easier to document and reason about a
function that does one thing, than one that tries to do everything. Which
would you rather work with, individual functions for:

buy_ingredients
clean_kitchen_work_area
wash_vegetables
prepare_ingredients
cook_main_course
fold_serviettes 
make_desert
serve_meal
do_washing_up

etc., or one massive function:

prepare_and_serve_five_course_meal

Even if each function is only called once, maintenance is simpler if the
code is broken up into more easily understood pieces.

(5) Machine efficiency. This can go either way. Code takes up memory too,
and it may be easier for the compiler to work with 1000 small functions
than 1 big function. I've actually seen somebody write a single function so
big that Python couldn't import the module, because it ran out of memory
trying to compile it! (This function was *huge* -- the source code was many
megabytes in size.) I don't remember the details, but refactoring the
source code into smaller functions fixed it.

On the other hand, if you are tight for memory, 1 big function may have less
overhead than 1000 small functions; and these days, with even entry level
PCs often having a GB or more of memory, it is rare to come across a
function so big that the size of code matters. Even a 10,000 line function
is likely to be only a couple of hundred KB in size:

>>> text = '\n'.join('print x+i' for i in range(1, 10001))
>>> code = compile(text, '', 'exec')
>>> sys.getsizeof(code.co_code)  # size in bytes
90028


So that's four really good reasons for splitting code into functions, and
one borderline one, other than code re-use. There may be others.


-- 
Steven




More information about the Python-list mailing list