Python Programming Idioms And Conventions - Part 2

As a follow-up to the previous article, let's discuss other idioms, or simply good practices, that Python developers use on a daily basis for their projects.

Testing that a sequence is not empty

Let's say you have a list mylist that can take an arbitrary value and you want to test if it is not empty.

You may write this in your code file:

if len(mylist) != 0:
    print(“Yes we are good!”)

That would be correct, but that is not needed for testing the "is not empty" case for a list. You would use what is called "truth testing" or "boolean testing" and write:

if mylist:
    print(“Yes we are good!”)

By doing that, because of the if, the mylist value is converted to a boolean, and that conversion would give False when the list is empty. You can confirm this by testing the following in the Python interpreter, using the bool() function:

>>> alist = []
>>> bool(alist)
False

The same applies to other sequences such as strings and tuples, and also to dictionaries.

To understand the general rule by which the bool function works, you can see this blog post: https://www.geeksforgeeks.org/bool-in-python/

Reversing a list

For reversing a list, you just use the .reverse() method as follows:

>>> mylist = [1, "hello", 5, 3]
>>> mylist.reverse()
>>> mylist
[3, 5, 'hello', 1]

As you can see, the list is reversed in place.

What about copying the list before reversing it?

We just saw that .reverse() reverses the list in place. So you may ask: What if I want to keep the value of original list? The answer is: Make a copy, just before doing the reverse action. You can do that using the list() function, as follows:

>>> mylist = [1, "hello", 5, 3]
>>> my_old_list = list(mylist)
>>> mylist.reverse()
>>> mylist
[3, 5, 'hello', 1]
>>> my_old_list
[1, 'hello', 5, 3]

There are other possible ways to copy the list such as using the .copy() method (my_old_list = mylist.copy()) or using slicing (my_old_list = mylist[:]).

With these three copying techniques, if there are lists or class instances in the list, the result of the copy operation will just point to them, they will not be copied.

Deep-copying a list

Here is an example showing how we deep-copy a list:

>>> import copy
>>> foo = [3, 5, 'hello']
>>> bar = [1, foo, 5, 3]
>>> baz = copy.deepcopy(bar)
>>> baz
[1, [3, 5, 'hello'], 5, 3]

The following helps better understand what is going on here:

>>> id(foo)
140186458069576
>>> for i in baz:
...     print(id(i))
... 
94634996872160
140186458070728
94634996872288
94634996872224
>>> for i in bar:
...     print(id(i))
... 
94634996872160
140186458069576
94634996872288
94634996872224

An execution on my computer gives that: The memory reference of the [3, 5, 'hello'] list member of baz (the result of the "deepcopy" operation) is 140186458070728 while the memory reference of the same value in its original instance is 140186458069576.

Fun with dictionaries

To iterate through a dict (called d in the code), we use the key as the iteration variable:

for key in d:
    print(d[key])

To check if a key is in a dict, use the if key in d: test, as follows:

if key in d:
    print(True)

To add a key to the dict if it does not exist:

if key not in d:
    d[key] = value

To get a fallback value (for example for display) when a key does not exist in the dict, there is a get() method that gives flexibility:

value = d.get(key, "missing value")

So, we can use the empty string, for example:

value = d.get(key, "")

At ease with iterators

When you know that you are dealing with an iterator, one classic thing is that you iterate over it.

for i in myiterator:
    # do something

Notice that, in the case of a for loop, if your iterator eventually returns a list, you should not convert it to a list (by doing list(myiterator)). Why? Because the for loop takes care of everything, and manipulating an iterator instead of a list has the advantage of not loading the whole list in memory, which saves memory.

By the way, you know it is an iterator, because the object has the special methods __iter__() and __next__, which you can see by doing dir(myiterator).

Another nice thing with the iterator is the .next() method. I have seen this useful when you get a CSV reader object, which is an iterator, and you want to skip the first row or entry:

with open("names.csv", "r") as f:
    data = csv.DictReader(f)

    # We want to skip line of bad data
    next(data)

    for row in data:
        print(f"{row['firstname']} {row['lastname']}")

Conclusion

That's it for today! And you actually know a lot already.

These idioms, in addition to showing how a Python developer writes code, also show the way he thinks for the outcome of efficient programs or readable quality code.

comments powered by Disqus

Need help for your project?

Our team can contribute to your project, working on a specific task, or doing all the coding based on your specifications, using Python, a web framework such as Django or a CMS.