Programming with Python: best practices, pitfalls and tips...

18 May 2020
bien-programmer-en-python
Programming with Python is an art in itself. The author of this guide takes a look at the pitfalls to avoid, and gives you a few useful tips to put into practice.

Python language dialects

Here are the main Python dialects. The author’s only experience is with PyPy, basically used in an attempt to make some programs run faster.

The GIL (“Global Interpreter Lock”) is a limitation of Python which prevents a number of small processes (“threads”) from being efficiently executed in parallel.

dialects_python_language

 

The Micro Python implementation makes a number of drastic cuts to the languages in the interests of optimisation. It would be interesting to explore porting programs to this dialect in anticipation of achieving performance gains (for traditional applications) – an area worth exploring.

 

Let’s look at the language in more detail

 

The virtue of indentation

Python’s most distinctive feature compared to other languages is its decision to add semantic meaning to indentation.

For example, here is an example using code for the Fibonacci function:

comparison_c_language_and_python_language

We can see that Python does not require the “;” terminator which must end every instruction in C. This is the first improvement in readability, but by no means the only one.

We can also see that C uses square brackets {and} to delimit blocks, and that the C compiler accepts arbitrary indentations (which makes the code less readable).

identation_virtue_language_python

For example, the two following Python programs will not return the same result:

identation_virtue_language_python_table_2

Best practice in Python3 is to indent using 4-space blocks. By definition, a Python program cannot be wrongly indented, as indentation has a semantic, rather than decorative, purpose.

This indentation philosophy is one of the main criticisms voiced by detractors of the language (especially if they use an unsuitable editor that mixes spaces and tabs in an uncontrolled way).

 

To mutate or not to mutate, that is the question

In Python, a “mutable” type is an object type that can be changed, whereas an “immutable” type cannot.

Below are the language’s main native types (we refer here to Python 3; some descriptions are different in Python 2).

mutable_or_not_mutable_language_python

Note 1: there is no limit on the value of integers. Developers need not be concerned with choosing short, int and long types, which in any case may have different constraints depending on the machine used.

We can use the following example to illustrate the benefits of this (the author has intentionally entered integer values which exceed the storage capacities of the Java and C languages):

mutable_or_not_mutable_language_python_table_3

Note 2: there is no requirement for homogeneity in a sequence’s types (which is somewhat different to other languages)

Note 3: the str type is not a character array. It is possible to switch between a str type and a bytes type using the encode and decode functions:

encode_and_decode_python_language

This characteristic distinguishes Python3 from Python2.

Note 4: the dict type, which is widely used in Python, offers rapid access to large data tables because the implementation ensures that the value can be calculated rapidly from the key. In the same way, testing for the presence of an element in a set is a quick process.

 

Dynamic typing: if it quacks, it’s a duck

In Python, variables are not typed; the interpreter “uses its common sense” to identify the type from the variable.

 

l = ['a', 'b', 'c']

type(l)  → <class 'list'>

 

In Python, a variable can change type:

 

l = ['a', 'b', 'c']

l = ('a', 'b', 'c') # Now a  Tuple

 

Disadvantage: static typing known at compile time allows many options for optimisation, which is therefore impossible in Python. In addition, a change in type is often a sign of an inadvertent error by the programmer which is not detected in Python, but would have been in a statically typed language. Type constraints in languages which are statically typed (defined before compilation) allow coding errors to be detected. At the end of the article, we will return to this aspect and how this pitfall can be mitigated.

In a statically-typed language, these two constructions would not have been possible.

In the C language, the equivalent of the first would be:

char *l[3] = {«a», «b», «c»} // type of ‘l’ is array of pointer to char

 

Assignment and scope

In Python, a variable is an “object reference”.

The following code:

b = Dog()

a = b

thus means that a and b reference the same object of class Dog.

In C++, this would be called an “alias”.

 

In addition, it is possible to write:

a = b = c # multiple assignment

and:

a, b = b, a  # simultaneous exchange of the values of the two variables

The scope of a variable is the entire source code in which it is accessible. It is sometimes necessary to help the interpreter to understand this scope, as illustrated by the following code:

assignment_and_scope_python_language

In Python, by default, assigning means creating a new variable; where this is not the case, therefore, the interpreter needs to be notified.

 

Equality and identity

Python programmers sometimes find themselves wondering whether to use equality or identity. Equality is compared using == and identity using is.

observation_conclusion_python_language_table

Anecdotally, the standard Cpython interpreter uses the address of an object to define its identity (which other languages do explicitly).

We will not examine the language in any more detail in this presentation – we have done enough of this already (this article is not a Python course).

 

What Python doesn’t have...

Here are a few omissions in Python, which can frustrate programmers coming from other languages.

 

Constants

Python does not use constant variables.

 

const int n = 555; // C++ language

final int n = 666; // JAVA language

 

“Switch case”

Python does not implement switch case statements (vetoed by Guido van Rossum)

switch_case_python_language

Encapsulation... a lax approach

Encapsulation is the process of hiding data “inside” a logical block.

Python uses a permissive encapsulation philosophy. It does not entirely block access to an object’s attributes and methods, but merely discourages access to them:

encapsulation_python_language

In a language such as Java and C++, some attributes would be marked “Public”, and are visible everywhere; “Protected” attributes are visible in some cases and not others; and “Private” attributes are visible only within the relevant class.  In these languages, accessing “Private” attributes from outside a class throws a compile error.

Additionally, in Python, there is no way of distinguishing between a file that displays features (visible to the user of a class or a library) and a file that implements them (sometimes hidden to that user). ADA uses specification and implementation files. The C and C++ languages have prototype and implementation files. This means that a change in implementation limits the compilations required in these languages.

But Python does not compile...

 

Python traps to avoid for non-Python users...

As a reminder, here are two classic traps (extracts from the Python AUSY Awards) provided by way of example:

traps_to_avoid_python

Conclusion

To conclude this series of articles, we would like to review in summary the various aspects we have examined throughout these 5 chapters.

During our investigation, the various qualities of this language were highlighted:

  • the ease, clarity and speed with which it can be learned in the first article;
  • its safety, full object implementation and extent of the libraries and tools in the fourth article.

The second article attempted to respond to the main criticisms traditionally levelled against it:

  • firstly, the gap between version two and version three;
  • and secondly, the slow execution.

Concerning the latter point, the third article has suggested a variety of approaches to optimise Python code and make it faster.

This fifth and last article has gone to the heart of the subject, and introduced the specifics of Python-language programming. It should be of value to developers.

Readers are invited to continue learning by looking in more depth at two aspects of the language which have been only briefly touched on in these articles:

  • Micropython, a subset of the language for embedded systems, which reduces the executable’s memory overhead.
  • Mypy, an annotations-based Python code type checker, which makes for more reliable development (used regularly by the author).

These two very recent innovations have taken this language into areas which the developer community had not anticipated. Until a few years ago, these areas had remained the preserve of traditional languages (C, C++ and Java, etc.).

 

 

jeremie_lefrancois_consultant_ausy
Jérémie Lefrançois, an Ausy consultant since 2004, wrote his first programmes in Basic on a ZX Spectrum back in 1982. He gained a postgraduate qualification in IT in 1989, and has worked with many computer languages on a personal and professional basis. He enjoys exploring and comparing the possibilities of different languages - especially Python - which has been his preferred language since 2014. He thus takes a keen interest in monitoring developments in this dynamic language, which he likes for its ease of implementation.

 

Don’t hesitate to check out our Big Data offer.

Let's have a chat about your projects.

bouton-contact-en