Is pythonic way the better way?
Here I have shared my thoughts on some of the pythonic way coding style. These are not design patterns (which are best practices used by experienced object-oriented software developers) of any sort, just some ways of writing code differently or I should say more pythonic.
If you might not already know - Python is an interpreted, interactive, object-oriented programming language. But more importantly, Python is a programming language that lets you work more quickly and integrate your systems more effectively.
If you want to learn all the recommended python style conventions/guidelines, you can learn here. So with that, let’s start with something which you might already know.
Swapping of values
This is a cool way of swapping values among two variables. Python does not create any temporary variables. It is all done on the stack.
a, b = b, a
This tuple unpacking not only makes code more readable but also handles the state better.
List comprehension
Most people working with python at a certain point of time discover about list comprehensions which are shorthand syntax of writing for a loop.
a = [i**2 for i in range(10)]
Awesome right, short and simple. If you use round brackets ()
instead of square brackets []
instead of returning a completely new list, it returns a generator object. Generators in python are super cool, they yields one value at a time which requires less memory. Any python function with a keyword “yield” may be called a generator. When the generator encounters a yield keyword the state of the function is frozen and all the variables are stored in memory until the generator is called again.
>>> a = (i**2 for i in range(10))
>>> print(a)
<generator object <genexpr> at 0x0000025CADCFA518>
>>> print(sum(a))
285
This is where the shortcomings of pythonic ways are prevalent amongst beginners, most beginners often use square brackets even when not required.
Compound comparison statements
A compound statement consists of one or more ‘clauses’. An example for this will be
>>> x = 5
>>> 10 < x < 20
False
>>> x = 15
>>> 10 < x < 20
True
>>> x = 25
>>> 10 < x < 20
False
A compound comparison is a neat feature in python. In other languages, you would need to express this as two different comparisons joined with an and
operation
>>> x = 15
>>> (10 < x) and (x < 20)
True
Distinguishing multiple exit points in loops
This is sort of like a replacement for flag variables. There are two ways you can exit out of the for loop 1) You hit the break; or 2) You did not. In the second case, else block
will get executed.
>>> def find(seq, target):
... for i, value in enumerate(seq):
... if value == target:
... break
... else:
... return -1
... return i
...
>>> find([3,4,5], 5)
1
>>> find([3,4,5], 6)
-1
I do not think there is any reason not to use this except the poor naming of else. I have not seen this used in many codebases maybe because it has a very niche use case, unlike list comprehension.
Ternary operator
Blueprint of general syntax
(if_test_is_false, if_test_is_true)[test]
Example usage
a = ('Odd', 'Even')[num//2 == 0]
This sounds a lot useful and you wonder why you might not have heard of this before or seen something like this in some codebase. There is a reason behind this, to understand consider this example
>>> (print('isFalse'), print('isTrue'))[True]
isFalse
isTrue
As you can see it prints both cases. Because of this not only extra processing is wasted but it can introduce some bugs. Ternary if else cannot be used to handle None value (or null value for non-python world).
>>> import re
>>> message = 'Is pythonic way the better way?'
>>> match = re.search(r'python', message)
>>> (match.group(), 'Not found')[match == None]
'python'
>>> match = re.search(r'python3', message)
>>> (match.group(), 'Not found')[match == None]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'
We received this error because when re.search fails, it returns None
and we called a method
on the something which was None
. This happens because with the tupled ternary technique, the tuple is first built, then an index is found as a result both True
case along with False
gets executed.
So use this with a lot of care because the way I see this, it not a replacemrnt for if-else!
Walrus operator available from Python 3.8 (PEP 572)
Walrus operator can be used to consolidate an assignment statement and a boolean expression when both assignment and expression would utilize a similar statement.
>>> import re
>>> message = 'Is pythonic way the better way?'
>>> if(match := re.search(r'python', message)):
... print(match.group())
...
python
>>> if(match := re.search(r'python3', message)):
... print(match.group()) # No output
...
Parentheses are important as without that it would assign result of boolean expression instead of the statement.
>>> my_list = [1,2,3,4,5]
>>> if (n := len(my_list)) > 3:
... print(n)
...
5
>>> if n := len(my_list) > 3: # without parenthesis
... print(n)
...
True
Some good reads
- This article has been inspired by a talk from PyCon US 2013 by Raymond Hettinger go watch it here
- Mind-bending edge cases in python that make you say “Wat‽” go watch it here
- There is a fun project attempting to explain what exactly is happening under the hood for some counter-intuitive snippets and lesser-known features in Python which you can read here
Thank you!
I hope you find this article insightful, please share it with your friends, you can read my other articles at my blog
If I have missed something, please write a comment and I will update my article.