Tips for code cleanup and program optimization

Let's now spend some time discussing the tips and tricks that will help improve the performance of our Python program. In a normal case scenario of GUI programming, this generally involves speeding up sections of program that contribute to improving the overall user experience.

Program optimization is often obsessively taken as an exercise in reducing code execution time. For programs where timing is a crucial factor, this obsession is genuine. However, if you are developing a simple GUI application, a correct and consistent user experience is generally more important than mere fast user experience.

Trying to optimize a code even before it is functional is premature optimization and should be avoided. However, a GUI program with correct but considerably long response time probably needs to be optimized, and this is the subject of discussion of the following sections.

Choose the right data structure

Selecting the right data structure can have a profound impact on the performance of a program. If your program is to spend considerable time on lookups, use a dictionary, if feasible. When all you need is to traverse over a collection, prefer to choose a list over dictionaries, because dictionaries take more space.

When your data is immutable, prefer to choose tuples over lists, because tuples can be traversed faster than lists.

Working with Variables

The way you select variables in your program can considerably affect the speed of the execution of your program. For instance, if you do not need to change the content or attributes of a widget after its instantiation, do not create a class-wide instance of the widget.

For example, if a Label widget is to remain static, use Label(root, text='Name').pack(side=LEFT), instead of using the following snippet:

self.mylabel = Label(root, text='Name')
self.mylabel.pack(side=LEFT)

Similarly, do not create local variables if you are not going to use them more than once. For example, use mylabel.config (text= event.keysym) instead of first creating a local variable key and then using it only once:

key = event.keysym
mylabel.config (text=key)

If the local variable is to be used more than once, it may make sense to create a local variable.

Using Exceptions

Now here is a small caveat. In order to concentrate on illustrating core Tkinter concepts, we have deliberately ignored the clean exception handling in all our examples in this book.

We have implemented a "catch all errors" exception using simple try-except blocks in most of our projects. However, when programming your applications, you would ideally want to be as specific as possible about the exception you want to handle.

Python follows the EAFP (easier to ask for forgiveness than permission) style of coding, as opposed to the LBYL (look before you leap) style followed by most other programming languages.

Thus, using exception handling similar to the following one is normally cleaner in Python than checking conditions using the if-then block:

try:
    doSomethingNormal()
except SomethingWrong:
    doSomethingElse()

An example of an if-then block is shown in the following code snippet:

if SomethingWrong:
    doSomethingElse()
else:
    doSomethingNormal()

Filter and map

Python provides two built-in functions named filter and map to manipulate lists directly, rather than having to directly iterate over each item in the list. The filter, map, and reduce functions are faster than using loops, because a lot of the work is done by the underlying code written in C.

  • Filter: The filter(function, list) function returns a list (iterators in Python 3.x) that contains all the items for which the function returns a true value. For example:
    print filter(lambda num: num>6, range(1,10))# prints [7, 8, 9]

    This is faster than running a conditional if-then check against the list.

  • Map: The map(func, list) function applies func to each item in the list and returns the values in a new list (returns iterators instead of lists in Python 3.x). For example:
    print map(lambda num: num+5, range(1,5)) #prints [6, 7, 8, 9]

    This again is faster than running the list through a loop, adding 5 to each element.

Profiling

Profiling involves generating detailed statistics to show how often and for how long various routines of a program execute. This helps is isolating offending parts of a program, and those parts probably need redesigning.

Python 2.7.x provides a built-in module named cProfile, which enables generation of detailed statistics about a program. The module gives details such as the total program-running time, time taken to run each function, and the number of times each function is called. These statistics make it easy to determine the parts of code that need optimization.

Note

In particular, cProfile provides the following data for a function or script:

  • ncalls: The number of times a function is called
  • tottime: The time spent on a function, excluding time spent on calling other functions
  • percall: tottime divided by ncalls
  • cumtime: The time spent on a function, including calls to other functions
  • percall: cumtime divided by tottime

You can profile an individual function with the help of this:

import cProfile
cProfile.run('spam()','spam.profile')

You can then view the results of profiling using another module called pstats:

import pstats
stats = pstats.Stats('spam.profile')
stats.strip_dirs().sort_stats('time').print_stats()

More importantly, you can profile an entire script. Let's say you want to profile a script named myscript.py. You simply navigate to the directory of the script using a command-line tool, and then type and run:

Python -m cProfilemyscript.py

This produces an output similar to the following:

1014 function calls in 0.093 CPU seconds
Ordered by: standard name
ncallstottimepercallcumtimepercallfilename:lineno(function)
1    0.000    0.000    0.000    0.000 Tkinter.py:3686(Studbutton)
1    0.000    0.000    0.000    0.000 Tkinter.py:3694(Tributton)
416  0.001    0.000    0.002    0.000 Tkinter.py:74(_cnfmerge)
1    0.011    0.011    0.028    0.028 myscript.py:19(<module>)
2    0.058    0.029    0.086    0.043 myscript.py:20(setAge)
7520.105   0.0000.257    0.129 myscript.py:23(findAudio)
10.001    0.001    0.013    0.013 myscript.py:25(createGUI)
1    40.004   0.000    0.005    0.005 myscript.py:4(saveAudio)
1    0.000    0.000    0.000    0.000 myscript.py:49(<module>)

After this, you can analyze the code to see the functions that take more time to execute. In our hypothetical example in the preceding output, we notice that the functions findAudio and saveAudio take the maximum time to execute. We can then analyze these two functions to see if they can be optimized.

In addition to the cProfile module, there are other modules, such as PyCallGraph and objgraph , and they provide visual graphs for profile data.

Other Optimization Tips

Optimization is a vast topic and there is a lot that you can do. If you are interested in knowing more about code optimization, you might start with the official Python optimization tips at the following link:

http://wiki.python.org/moin/PythonSpeed/PerformanceTips

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.30.19