Idiomatic Python - miscellaneous part 1¶

Comprehensions¶

In [1]:
original_data = (1, 2, 3, 4) 

Don't do this.

In [2]:
# list square_roots_list = [] for val in original_data: square_root = val ** (1 / 2) square_roots_list.append(square_root) print(square_roots_list) # set square_roots_set = set() for val in original_data: square_root = val ** (1 / 2) square_roots_set.add(square_root) print(square_roots_set) # dict square_roots_dict = {} for val in original_data: square_root = val ** (1 / 2) square_roots_dict[val] = square_root print(square_roots_dict) # dict with a condition integer_square_roots_dict = {} for val in original_data: square_root = val ** (1 / 2) if square_root.is_integer(): integer_square_roots_dict[val] = square_root print(integer_square_roots_dict) 
[1.0, 1.4142135623730951, 1.7320508075688772, 2.0] {1.0, 2.0, 1.7320508075688772, 1.4142135623730951} {1: 1.0, 2: 1.4142135623730951, 3: 1.7320508075688772, 4: 2.0} {1: 1.0, 4: 2.0} 

Note: in case you're using 2.X version of Python for some reason, the result of 1/2 is 0 instead of 0.5.

Use comprehensions!¶

In [3]:
square_roots_list = [val ** (1 / 2) for val in original_data] print(square_roots_list) square_roots_set = {val ** (1 / 2) for val in original_data} print(square_roots_set) square_roots_dict = {val: val ** (1 / 2) for val in original_data} print(square_roots_dict) integer_square_roots_dict = { val: val ** (1 / 2) for val in original_data if (val ** (1 / 2)).is_integer() } print(integer_square_roots_dict) 
[1.0, 1.4142135623730951, 1.7320508075688772, 2.0] {1.0, 2.0, 1.7320508075688772, 1.4142135623730951} {1: 1.0, 2: 1.4142135623730951, 3: 1.7320508075688772, 4: 2.0} {1: 1.0, 4: 2.0} 

Using in for checking presence of an element in a collection¶

In [4]:
name = "John Doe" 

Don't do it like this.

In [5]:
if name == "John" or name == "Doe" or name == "John Doe": print("This seems to be our guy") 
This seems to be our guy 

Do it like this!¶

In [6]:
if name in ("John", "Doe", "John Doe"): print("This seems to be our guy") 
This seems to be our guy 

Chained comparisons¶

In [7]:
a, b, c, d = 1, 2, 3, 4 

Don't do it like this.

In [8]:
if b > a and c > b and d > c: print("from lowest to highest: a, b, c, d") 
from lowest to highest: a, b, c, d 

Do it like this!¶

In [9]:
if a < b < c < d: print("from lowest to highest: a, b, c, d") 
from lowest to highest: a, b, c, d 

Falsy/truthy values¶

In [10]:
# These are falsy my_list = [] my_dict = {} my_set = set() my_tuple = tuple() zero = 0 false = False none = None my_str = "" # Basically the rest are truthy # for example: my_second_list = ["foo"] 

Don't do it like this.

In [11]:
if len(my_list) == 0: print("Empty list is so empty") if not len(my_dict): print("Empty dict is also very empty") if not len(my_set) and not len(my_tuple): print("Same goes for sets and tuples") if not bool(zero) and not bool(false) and not bool(none) and len(my_str) == 0: print("These are also falsy") if len(my_second_list) > 0: print("This should be true") 
Empty list is so empty Empty dict is also very empty Same goes for sets and tuples These are also falsy This should be true 

This is much better!¶

In [12]:
if not my_list: print("Empty list is so empty") if not my_dict: print("Empty dict is also very empty") if not my_set and not my_tuple: print("Same goes for sets and tuples") if not zero and not false and not none and not my_str: print("These are also falsy") if my_second_list: print("This should be true") 
Empty list is so empty Empty dict is also very empty Same goes for sets and tuples These are also falsy This should be true 

any & all¶

In [13]:
example_collection = ["a", True, "Python is cool", 123, 0] 

Don't do it like this.

In [14]:
any_value_truthy = True for val in example_collection: if val: any_value_truthy = True break all_values_truthy = True for val in example_collection: if not val: all_values_truthy = False break print(f"any truthy: {any_value_truthy}, all truthy: {all_values_truthy}") 
any truthy: True, all truthy: False 

Do it like this!¶

In [15]:
any_value_truthy = any(example_collection) all_values_truthy = all(example_collection) print(f"any truthy: {any_value_truthy}, all truthy: {all_values_truthy}") 
any truthy: True, all truthy: False 

Pythonic substitute for ternary operator¶

Many other programming languages have a ternary operator: ?. A common use case for the ternary operator is to assign a certain value to a variable based on some condition. In other words, it could be used like this:

variable = some_condition ? some_value : some_other_value 

Instead of doing this.

In [16]:
some_condition = True # just a dummy condition if some_condition: variable = "John" else: variable = "Doe" print(variable) 
John 

You can do it like this!¶

In [17]:
variable = "John" if some_condition else "Doe" print(variable) 
John 

Function keywords arguments¶

For better readability and maintainability.

In [18]:
def show_person_details(name, is_gangster, is_hacker, age): print(f"name: {name}, gangster: {is_gangster}, hacker: {is_hacker}, age: {age}") 

This is not good. It's hard to tell what True, False and 83 refer here if you are not familiar with the signature of the show_person_details function.

In [19]:
show_person_details("John Doe", True, False, 83) 
name: John Doe, gangster: True, hacker: False, age: 83 

This is much better!¶

In [20]:
show_person_details("John Doe", is_gangster=True, is_hacker=False, age=83) 
name: John Doe, gangster: True, hacker: False, age: 83 

Extra: keyword only arguments after *¶

This might be useful for example if the signature of the function is likely to change in the future. For example, if there's even a slight chance that one of the arguments may be dropped during the future development, consider using *.

In [21]:
def func_with_loads_of_args(arg1, *, arg2=None, arg3=None, arg4=None, arg5="boom"): pass # This won't work because only keyword arguments allowed after * # func_with_loads_of_args('John Doe', 1, 2) # This is ok func_with_loads_of_args("John Doe", arg4="foo", arg5="bar", arg2="foo bar") 

Multiple assigment¶

Let's say we want to swap the values of two variables.

Don't do it like this.

In [22]:
# original values a = 1 b = 2 # swap tmp = a a = b b = tmp print(a, b) 
2 1 

Do it like this!¶

In [23]:
# original values a = 1 b = 2 # swap a, b = b, a print(a, b) 
2 1 

(Un)packing¶

In [24]:
my_list = [1, 2, 3, 4, 5, 6] 

Don't do something like this.

In [25]:
first = my_list[0] last = my_list[-1] middle = my_list[1:-1] print(first, middle, last) packed = [first] + middle + [last] assert packed == my_list 
1 [2, 3, 4, 5] 6 

This is the Pythonic way!¶

In [26]:
# unpacking first, *middle, last = my_list print(first, middle, last) # packing packed = [first, *middle, last] assert packed == my_list 
1 [2, 3, 4, 5] 6