An Introduction to Python Part 3
By Jeff Tranter | Tuesday, July 12, 2016
In this, the final installment of the blog series on Python, we'll look at modules and how object-oriented programming is supported. We'll take a brief look at two of the popular bindings for Qt from Python: PyQt and PySide. I'll wrap things up with some tips for effective programming and references for learning more.
Python includes a set of standard libraries, divided into packages and modules.
A module is a collection of Python definitions and statements and possibly a main program (which does not automatically get run when the module is imported).
A package is a container for multiple modules, located under a common directory. Python searches for modules in standard locations, including a search path set by the PYTHONPATH environment variable.
The import statement adds a symbol to your namespace, which can be a module, class, function or object. Entire modules can be imported with:
You can import individual symbols from a module using:
from moduleName import symbol
There are too many standard Python and third-party modules to list here, but some commonly used modules include collections, datetime, decimal, math, os, re, string, struct, sys, and time.
Python supports object-oriented programming through classes and methods. All classes derive from a base class, all of which ultimately derive from object.
There is support for multiple inheritance and any method can be overridden in a subclass. (All methods are virtual.)
There is no support for method overloading, no abstract classes, and no access control (i.e. public/private/protected as in C++). It does support a form of operator overloading via "special methods."
Python is fundamentally a scripting language. While Python fully supports object-oriented programming, it is rarely required for small programs. To use Qt from PyQt or PySide you need to write object-oriented code. I will cover some of the basics here.
Here is an example class definition:
class Rectangle(object): """This class represents a rectangle.""" def __init__(self, width, height): self.width = width self.height = height def getWidth(self): return self.width def setWidth(self, width): self.width = width def getHeight(self): return self.height def setHeight(self, height): self.height = height def area(self): return self.getWidth() * self.getHeight()
You define a class with the class keyword, specifying the parent (or base) class -- in this case object. Methods are defined as functions inside the scope of the class. The method __init__ is the initializer method, run when an instance is created.
The multiline string in triple quotes is a convention for documenting classes called a docstring. As well as providing documentation, it can be extracted and used by tools like IDEs.
Any variables and methods prefixed with an underscore are considered private by convention but this is not enforced by Python.
Given the above class definition we could use it in code, as in these examples:
rect = Rectangle(10,20) rect.setWidth(640) rect.setHeight(480) print("width =", rect.width) # Directly read attribute print("height =", rect.getHeight()) # Call method print("area =", rect.area())
We can instantiate an object using the class name and access its attributes or call methods. Here is a more elegant example that defines height, width and area as properties:
class Rectangle(object): """This class represents a rectangle.""" def __init__(self, width, height): self.width = width self.height = height def _width(self): return self._width def _setWidth(self, width): self._width = width width = property(fget=_width, fset=_setWidth) def _height(self): return self._height def _setHeight(self, height): self._height = height height = property(fget=_height, fset=_setHeight) def _area(self): return self.width * self.height area = property(fget=_area)
We can then access the properties using code such as:
rect = Rectangle(10,20) rect.width = 640 rect.height = 480 print("width =", rect.width) print("height =", rect.height) print("area =", rect.area)
Python provides a number of special methods, a few of which are summarized below:
|Comparison operator:||__cmp__(self, args)|
|Addition operator:||__add__(self, other)|
There are other special methods, such as those for adding support for collections and iterators. Many of these have default implementations. For example, the default comparison operator simply compares object attributes.
Static methods are supported by putting what is known as a decorator before the method definition: @staticmethod. There is also a @classmethod decorator.
As we have seen, you must specify the class to inherit from in the class definition. You can access the parent or base class using super(). A subclass can override methods in the parent class and Python will take care of calling the correct method. It is permitted to inherit from multiple classes.
Here is a small but complete example program illustrating inheritance. I encourage you to study it and try running it on your system.
class Base(object): def __init__(self, id=1): self.id = id def _id(self): return self._id def _setId(self, id): self._id = id def method1(self): print("in Base method1") id = property(fget=_id, fset=_setId) class Sub(Base): def __init__(self, id=2): super(Sub, self).__init__(id) # def method1(self): # print("in Sub method1") b1 = Base(1234) print("b1 id =", b1.id) s1 = Sub(2345) print("s1 id =", s1.id) b1.method1() s1.method1()
You can call method1 on an instance of class Sub and see that it calls the method in the base class. If we uncomment the definition of method1 in class Sub, it will then call the method in the subclass.
PyQt (3) is a set of Python version 2 and 3 bindings for Qt 4 and 5. It runs on all platforms supported by Qt. It is developed by Riverbank Computing and is dual licensed under the GNU GPLv3 and a commercial license.
Unlike Qt, PyQt is not available under the GNU Lesser General Public License (LGPL) so using it with proprietary applications generally requires the commercial version of PyQt.
Pyside (4) is another Python binding for Qt. It is released under the LGPL so it can be used for both open source and proprietary software.
It was originally developed by Nokia after they failed to reach an agreement with Riverbank Computing to change the PyQt licensing terms to include the LGPL as an alternative license. Support from Nokia ended when they sold Qt to Digia in 2012, but it continues to be maintained by the open source community.
Originally developed for Qt 4, Qt 5 support is not yet very complete or stable and is in a different version currently called PySide2.
PySide and PyQt are similar as far as programming. Both are actively maintained and have a complete development toolchain including support for Qt Designer user interface files.
PyQt and PySide expose the entire Qt API to Python using the same class and method names as in C++. Both are included in many Linux distributions like Ubuntu. Here is an example of a small PySide program:
#!/usr/bin/python import sys from PySide.QtCore import * from PySide.QtGui import * app = QApplication(sys.argv) button = QPushButton("Hello World") app.connect(button, SIGNAL("clicked()"), app, SLOT("quit()")) button.show() app.exec_() sys.exit()
The example should be familiar to users of Qt from C++. Note that it can be run directly by the Python interpreter. The same example will work with PyQt if you change the import statements from PySide to PyQt4. (I will cover more of PyQt or PySide in future blog posts.)
If you don't need to use Qt and only want to call C++ code from Python, or vice versa, you might want to investigate the Boost Python Library framework (6).
New to Python? Here are a few tips:
PEP 8 is a Python style guide. I highly recommend you follow it. There is a tool (written in Python) that checks your code for compliance, making it easy to identify and fix violations.
If you have a choice, Python 3 is recommended over Python 2 for new code. On many Linux systems (e.g. Ubuntu) the python command may run Python 2 and you need to run python3 to get version 3. A significant change to note from Python 2 to 3 is that print() became a function in Python 3 and needs parentheses around the arguments.
As you may have have noticed in some of the earlier examples, the Linux "shebang" feature allows Python scripts to be run directly if you specify the name of the interpreter in the first line.
Interactive mode is useful for learning and debugging. For large-scale programming there are Integrated Development Environments (IDE) that support Python.
I've found that if you want to do something in Python, someone has probably written a module to do it so always check for existing third-party modules before reinventing the wheel.
I've used many scripting languages over the years, including shell languages, Perl, and Tcl. I consider Python my favorite. It provides a very good balance between readability, performance, and suitability for both very small and very large programs.
And I’m not the only one enamored with Python. It frequently appears at or near the top in lists of popular programming languages (it is currently number two on GitHub). Despite being around for 25 years, Python’s popularity has grown so much of late it recently overtook Java as the language of choice in introductory programming courses. Eight of the top 10 university computer science departments are now using Python to teach coding -- proof of its power.
- Python Software Foundation website, last accessed 15 Jun 2016, https://www.python.org/
- Python Resources, Full Stack Python website, last accessed 15 Jun 2016, http://www.fullstackpython.com/best-python-resources.html
- What is PyQt?, Riverbank Computing website, last accessed 15 Jun 2016, https://www.riverbankcomputing.com/software/pyqt/intro
- PySide - Python for Qt, PySide website, last accessed 15 Jun 2016, https://wiki.qt.io/PySide
- Rapid GUI Programming with Python and Qt, Mark Summerfield, published book, https://www.amazon.com/Programming-Python-Prentice-Software-Development/dp/0132354187
- Boost.Python library, Boost C++ Libraries website, last accessed 15 Jun 2016, http://www.boost.org/doc/libs/1_61_0/libs/python/doc/html/index.html