10 способов как сделать ваш Python код лучше

1. Итерация с помощью enumerate() вместо range(len())

Если нам нужно выполнить итерацию по списку и отследить как индекс, так и текущий элемент, большинство людей будут использовать range(len). Но гораздо приятнее использовать встроенную функцию enumerate. Она возвращает как текущий индекс, так и текущий элемент в виде кортежа. Так что мы можем напрямую проверить значение, а также получить доступ к элементу с индексом.

data = [1, 2, -3, -4]
# weak:
for i in range(len(data)):
    if data[i] < 0:
        data[i] = 0

# better:
data = [1, 2, -3, -4]
for idx, num in enumerate(data):
    if num < 0:
        data[idx] = 0

2. Использовать list comprehension вместо необработанных циклов

Допустим, мы хотим создать список с определенными значениями, например, список со всеми квадратными числами от 0 до 9. Первое что приходит на ум было бы создать пустой список, затем использовать цикл for, сделать наш расчет и добавить его в список:

squares = []
for i in range(10):
    squares.append(i*i)

Но гораздо проще просто воспользоваться list comprehension. В этом случае потребуется только одна строчка, чтобы добиться того же результата:

squares = [i*i for i in range(10)]

И еще немного до кучи. Как вам такое? Ни чего подобного я не встречал ни где кроме Python.

# Generates a list containing values from 0 to 9
[i for i in range(10)]

# Generates a list of all even values from 0 to 9
[i for i range(10) if i % 2 == 0]

# Generates a list containing values from 1 to 10
[i + 1 for i in range(10)]

# Generates a list containing values from 0 to -9
[-i for i in range(10)]

# Generates all possible pairs between 0 and 9
[(a, b) for a in range(10) for b in range(10)]

# Shallow copies another list
my_list = [1, 3, 5, 7, 9]
[item for item in my_list]

3. Сортировка complex iterables с помощью встроенного метода sorted()

Если нам нужно отсортировать какой либо итерабельный файл, например, список, кортеж или словарь, то нам не нужно реализовывать алгоритм сортировки самостоятельно. Мы также можем использовать и встроенную функцию sorted. Она автоматически сортирует числа в порядке возрастания и возвращает новый список. Если мы хотим получить результат в порядке убывания, мы можем использовать аргумент reverse=True. И это работает с любым итерабельным параметром.

data = (3, 5, 1, 10, 9)
sorted_data = sorted(data, reverse=True) # [10, 9, 5, 3, 1]

Усложним. Например есть список, а внутри списка есть словари, и мы хотим отсортировать список по возрасту в словаре. Для этого мы также можем использовать функцию sorted, а затем передать ключ, который должен использоваться для сортировки. Ключ должен быть функцией, поэтому здесь мы можем использовать лямбда и использовать однострочную функцию, которая возвращает возраст.

data = [{"name": "Max", "age": 6}, 
        {"name": "Lisa", "age": 20}, 
        {"name": "Ben", "age": 9}
        ]
sorted_data = sorted(data, key=lambda x: x["age"])

4. Хранить уникальные значения с помощью Set

Если у нас есть список с несколькими значениями, а нам необходимо иметь только уникальные значения, то хорошая хитрость заключается в преобразовании нашего списка в Set. Множество [Set] - это неупорядоченный тип данных коллекции, который не имеет дубликатов, поэтому, в данном случае он удаляет все дубликаты.

my_list = [1,2,3,4,5,6,7,7,7]
my_set = set(my_list)

5. Сохраняем память с помощью генераторов

Допустим, у нас очень большой список с 10000 элементами, и мы хотим рассчитать сумму по всем элементам. Конечно, мы можем сделать это со списком, но у нас могут возникнуть проблемы с памятью. Это отличный пример, где мы можем использовать генераторы. Подобно пониманию списка, мы можем использовать понимание генератора, который имеет практически тот же синтаксис (отличаются только скобки). Генератор вычисляет один элемент за раз и только по запросу.

# list comprehension
my_list = [i for i in range(10000)]
print(sum(my_list)) # 49995000

# generator comprehension
my_gen = (i for i in range(10000))
print(sum(my_gen)) # 49995000

Если проверить список и генератор с помощью sys.getizeof(), то увидим значительную разницу. 80000 байт для списка против 128 для генератора. Имейте это ввиду когда работаете с большими данными.

import sys 

my_list = [i for i in range(10000)]
print(sys.getsizeof(my_list), 'bytes') # 87616 bytes

my_gen = (i for i in range(10000))
print(sys.getsizeof(my_gen), 'bytes') # 128 bytes

6. Определите значения по умолчанию в словарях с помощью .get() и .setdefault()

Допустим, у нас есть словарь с разными ключами. В какой-то момент в нашем коде мы хотим получить количество элементов, и мы предполагаем, что этот ключ также содержится в словаре. Когда мы просто попытаемся получить доступ к ключу, то он обрушит наш код. Так что лучший способ - использовать метод .get() в словаре.

my_dict = {'item': 'football', 'price': 10.00}
price = my_dict['count'] # KeyError!

# better:
price = my_dict.get('count', 0) # optional default value

.setdefault() возвращает значение по умолчанию, которое мы указали, и при следующей проверке словаря используемый ключ уже будет доступен в нашем словаре.

count = my_dict.setdefault('count', 0)
print(count) # 0
print(my_dict) # {'item': 'football', 'price': 10.00, 'count': 0}

7. Считать хэшируемые объекты с помощью collections.Counter

Если нам нужно посчитать количество элементов в списке, то в модуле collections есть то что надо. Нужно импортировать Counter из collections, а затем создать наш объект счетчика со списком в качестве аргумента. Если мы распечатаем это, то увидим сколько раз элемент появлялся в отсортированном виде. Если мы хотим получить счетчик для определенного элемента, то мы можем просто получить доступ к этому элементу, и он вернет соответствующий счетчик.

from collections import Counter

my_list = [10, 10, 10, 5, 5, 2, 9, 9, 9, 9, 9, 9]
counter = Counter(my_list)

print(counter) # Counter({9: 6, 10: 3, 5: 2, 2: 1})
print(counter[10]) # 3

Также у него есть очень удобный метод для возврата наиболее распространенных элементов most_common().

from collections import Counter

my_list = [10, 10, 10, 5, 5, 2, 9, 9, 9, 9, 9, 9]
counter = Counter(my_list)

most_common = counter.most_common(2)
print(most_common) # [(9, 6), (10, 3)]
print(most_common[0]) # (9, 6)
print(most_common[0][0]) # 9

8. Работа с f-строками [питон 3.6+]

Это появилось в Python 3.6 и это самый лаконичный способ форматирования строк. Мы просто можем написать f перед нашей строкой, а затем внутри строки используем фигурные скобки и обращаемся к переменным.

name = "Alex"
my_string = f"Hello {name}"
print(my_string) # Hello Alex

i = 10
print(f"{i} squared is {i*i}") # 10 squared is 100

9. Объединение словарей с помощью двойной звездочки ** [Python 3.5+]

Если у нас есть два словаря и мы хотим объединить их, мы можем использовать фигурные скобки и двойные звездочки для обоих словарей.

d1 = {'name': 'Alex', 'age': 25}
d2 = {'name': 'Alex', 'city': 'New York'}
merged_dict = {**d1, **d2}
print(merged_dict) # {'name': 'Alex', 'age': 25, 'city': 'New York'}

10. Упростите условную инструкцию if с помощью if x in list

Допустим, у нас есть список с основными цветами красного, зеленого и синего. А где-то в нашем коде появилась новая переменная, содержащая некоторый цвет, например c = red. И мы хотим проверить, не является ли это цветом из наших основных цветов:

colors = ["red", "green", "blue"]

c = "red"

# cumbersome and error-prone
if c == "red" or c == "green" or c == "blue":
    print("is main color")

Гораздо проще и намного лучше просто использовать синтаксис if x in list:

colors = ["red", "green", "blue"]

c = "red"

# better:
if c in colors:
    print("is main color")

Надеюсь, кому-нибудь это окажется полезным.